net: caif: added tegra specific caif for rainbow modem.
Sheshagiri Shenoy [Fri, 18 Mar 2011 00:35:49 +0000 (17:35 -0700)]
- added the tegra dependent layer of the caif protocol.
- integrated with the open source rainbow caif.
- verified the functionality using latest rainbow RIL.

bug 785523

Change-Id: I75be8d2ef6e5562facf902a3f963f34d241bb6c3
Reviewed-on: http://git-master/r/23421
Reviewed-by: Bharat Nihalani <bnihalani@nvidia.com>
Tested-by: Bharat Nihalani <bnihalani@nvidia.com>

Rebase-Id: R967aadae8836248af8c861a0d55c5ecc75198960

drivers/net/caif/Kconfig
drivers/net/caif/Makefile
drivers/net/caif/tegra_caif_sspi.c [new file with mode: 0644]
include/linux/tegra_caif.h [new file with mode: 0644]

index abf4d7a..3013a74 100644 (file)
@@ -47,3 +47,12 @@ config CAIF_HSI
        The caif low level driver for CAIF over HSI.
        Be aware that if you enable this then you also need to
        enable a low-level HSI driver.
+
+config TEGRA_SPI_CAIF
+       tristate "TEGRA specific CAIF SPI transport driver for slave interface"
+       depends on CAIF_SPI_SLAVE && TEGRA_SPI_SLAVE
+       default n
+       ---help---
+       The CAIF Link layer SPI Protocol driver for Tegra Slave SPI interface.
+       This driver implements a platform driver to accommodate for a
+       Tegra Slave SPI device.
index 91dff86..f307525 100644 (file)
@@ -13,3 +13,7 @@ obj-$(CONFIG_CAIF_SHM) += caif_shm.o
 
 # HSI interface
 obj-$(CONFIG_CAIF_HSI) += caif_hsi.o
+
+# Tegra specific SPI slave physical interfaces module
+tegra_cfspi_slave-objs := tegra_caif_sspi.o
+obj-$(CONFIG_TEGRA_SPI_CAIF) += tegra_cfspi_slave.o
diff --git a/drivers/net/caif/tegra_caif_sspi.c b/drivers/net/caif/tegra_caif_sspi.c
new file mode 100644 (file)
index 0000000..c2c15c1
--- /dev/null
@@ -0,0 +1,426 @@
+/*
+ * Copyright (c) 2011, NVIDIA Corporation.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/wait.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/spi/spi.h>
+#include <linux/tegra_caif.h>
+#include <mach/spi.h>
+#include <net/caif/caif_spi.h>
+
+MODULE_LICENSE("GPL");
+
+#define SPI_CAIF_PAD_TRANSACTION_SIZE(x) \
+       (((x) > 4) ? ((((x) + 15) / 16) * 16) : (x))
+
+struct sspi_struct {
+       struct cfspi_dev sdev;
+       struct cfspi_xfer *xfer;
+};
+
+static struct sspi_struct slave;
+static struct platform_device slave_device;
+static struct spi_device *tegra_caif_spi_slave_device;
+int tegra_caif_sspi_gpio_spi_int;
+int tegra_caif_sspi_gpio_spi_ss;
+int tegra_caif_sspi_gpio_reset;
+int tegra_caif_sspi_gpio_power;
+int tegra_caif_sspi_gpio_awr;
+int tegra_caif_sspi_gpio_cwr;
+
+
+static int __devinit tegra_caif_spi_slave_probe(struct spi_device *spi);
+
+static int tegra_caif_spi_slave_remove(struct spi_device *spi)
+{
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int tegra_caif_spi_slave_suspend(struct spi_device *spi
+                       , pm_message_t mesg)
+{
+       return 0;
+}
+#endif /* CONFIG_PM */
+
+#ifdef CONFIG_PM
+static int tegra_caif_spi_slave_resume(struct spi_device *spi)
+{
+       return 0;
+}
+#endif /* CONFIG_PM */
+
+static struct spi_driver tegra_caif_spi_slave_driver = {
+       .driver = {
+               .name = "baseband_spi_slave0.0",
+               .owner = THIS_MODULE,
+       },
+       .probe = tegra_caif_spi_slave_probe,
+       .remove = __devexit_p(tegra_caif_spi_slave_remove),
+#ifdef CONFIG_PM
+       .suspend = tegra_caif_spi_slave_suspend,
+       .resume = tegra_caif_spi_slave_resume,
+#endif /* CONFIG_PM */
+};
+
+void tegra_caif_modem_power(int on)
+{
+       static int u3xx_on;
+       int err;
+       int cnt = 0;
+       int val = 0;
+
+       if (u3xx_on == on)
+               return;
+       u3xx_on = on;
+
+       if (u3xx_on) {
+               /* turn on u3xx modem */
+               err = gpio_request(tegra_caif_sspi_gpio_reset
+                               , "caif_sspi_reset");
+               if (err < 0)
+                       goto err1;
+
+               err = gpio_request(tegra_caif_sspi_gpio_power
+                               , "caif_sspi_power");
+               if (err < 0)
+                       goto err2;
+
+               err = gpio_request(tegra_caif_sspi_gpio_awr
+                               , "caif_sspi_awr");
+               if (err < 0)
+                       goto err3;
+
+               err = gpio_request(tegra_caif_sspi_gpio_cwr
+                               , "caif_sspi_cwr");
+               if (err < 0)
+                       goto err4;
+
+               err = gpio_direction_output(tegra_caif_sspi_gpio_reset
+                                               , 0 /* asserted */);
+               if (err < 0)
+                       goto err5;
+
+               err = gpio_direction_output(tegra_caif_sspi_gpio_power
+                                               , 0 /* off */);
+               if (err < 0)
+                       goto err6;
+
+               err = gpio_direction_output(tegra_caif_sspi_gpio_awr
+                                               , 0);
+               if (err < 0)
+                       goto err7;
+
+               err = gpio_direction_input(tegra_caif_sspi_gpio_cwr);
+               if (err < 0)
+                       goto err8;
+
+               gpio_set_value(tegra_caif_sspi_gpio_power, 0);
+               gpio_set_value(tegra_caif_sspi_gpio_reset, 0);
+
+               msleep(800);
+
+               /* pulse modem power on for 300 ms */
+               gpio_set_value(tegra_caif_sspi_gpio_reset
+                               , 1 /* deasserted */);
+               msleep(300);
+               gpio_set_value(tegra_caif_sspi_gpio_power, 1);
+               msleep(300);
+               gpio_set_value(tegra_caif_sspi_gpio_power, 0);
+               msleep(100);
+
+               /* set awr high */
+               gpio_set_value(tegra_caif_sspi_gpio_awr, 1);
+               val = gpio_get_value(tegra_caif_sspi_gpio_cwr);
+               while (!val) {
+                       /* wait for cwr to go high */
+                       val = gpio_get_value(tegra_caif_sspi_gpio_cwr);
+                       pr_info(".");
+                       msleep(100);
+                       cnt++;
+                       if (cnt > 200) {
+                               pr_err("\nWaiting for CWR timed out - ERROR\n");
+                               break;
+                       }
+               }
+       }
+       return;
+err8:
+err7:
+err6:
+err5:
+       gpio_free(tegra_caif_sspi_gpio_cwr);
+err4:
+       gpio_free(tegra_caif_sspi_gpio_awr);
+err3:
+       gpio_free(tegra_caif_sspi_gpio_power);
+err2:
+       gpio_free(tegra_caif_sspi_gpio_reset);
+err1:
+       return;
+}
+
+static irqreturn_t sspi_irq(int irq, void *arg)
+{
+       /* You only need to trigger on an edge to the active state of the
+        * SS signal. Once a edge is detected, the ss_cb() function should
+        * be called with the parameter assert set to true. It is OK
+        * (and even advised) to call the ss_cb() function in IRQ context
+        * in order not to add any delay.
+        */
+       int val;
+       struct cfspi_dev *sdev = (struct cfspi_dev *)arg;
+       val = gpio_get_value(tegra_caif_sspi_gpio_spi_ss);
+       if (val)
+               return IRQ_HANDLED;
+       sdev->ifc->ss_cb(true, sdev->ifc);
+       return IRQ_HANDLED;
+}
+
+static int sspi_callback(void *arg)
+{
+       /* for each spi_sync() call
+        * - sspi_callback() called before spi transfer
+        * - sspi_complete() called after spi transfer
+        */
+
+       /* set master interrupt gpio pin active (tells master to
+        * start spi clock)
+        */
+       udelay(MIN_TRANSITION_TIME_USEC);
+       gpio_set_value(tegra_caif_sspi_gpio_spi_int, 1);
+       return 0;
+}
+
+static void sspi_complete(void *context)
+{
+       /* Normally the DMA or the SPI framework will call you back
+        * in something similar to this. The only thing you need to
+        * do is to call the xfer_done_cb() function, providing the pointer
+        * to the CAIF SPI interface. It is OK to call this function
+        * from IRQ context.
+        */
+
+       struct cfspi_dev *sdev = (struct cfspi_dev *)context;
+       sdev->ifc->xfer_done_cb(sdev->ifc);
+}
+
+static void swap_byte(unsigned char *buf, unsigned int bufsiz)
+{
+       unsigned int i;
+       unsigned char tmp;
+       for (i = 0; i < bufsiz; i += 2) {
+               tmp = buf[i];
+               buf[i] = buf[i+1];
+               buf[i+1] = tmp;
+       }
+}
+
+static int sspi_init_xfer(struct cfspi_xfer *xfer, struct cfspi_dev *dev)
+{
+       /* Store transfer info. For a normal implementation you should
+        * set up your DMA here and make sure that you are ready to
+        * receive the data from the master SPI.
+        */
+
+       struct sspi_struct *sspi = (struct sspi_struct *)dev->priv;
+       struct spi_message m;
+       struct spi_transfer t;
+       int err;
+
+       sspi->xfer = xfer;
+
+       if (!tegra_caif_spi_slave_device)
+               return -ENODEV;
+
+       err = spi_tegra_register_callback(tegra_caif_spi_slave_device,
+               sspi_callback, sspi);
+       if (err < 0) {
+               pr_err("\nspi_tegra_register_callback() failed\n");
+               return -ENODEV;
+       }
+       memset(&t, 0, sizeof(t));
+       t.tx_buf = xfer->va_tx;
+       swap_byte(xfer->va_tx, xfer->tx_dma_len);
+       t.rx_buf = xfer->va_rx;
+       t.len = max(xfer->tx_dma_len, xfer->rx_dma_len);
+       t.len = SPI_CAIF_PAD_TRANSACTION_SIZE(t.len);
+       t.bits_per_word = 16;
+       /* SPI controller clock should be 4 times the spi_clk */
+       t.speed_hz = (SPI_MASTER_CLK_MHZ * 4 * 1000000);
+       spi_message_init(&m);
+       spi_message_add_tail(&t, &m);
+
+       dmb();
+       err = spi_sync(tegra_caif_spi_slave_device, &m);
+       dmb();
+       swap_byte(xfer->va_tx, xfer->tx_dma_len);
+       swap_byte(xfer->va_rx, xfer->rx_dma_len);
+       sspi_complete(&sspi->sdev);
+       if (err < 0) {
+               pr_err("spi_init_xfer - spi_sync() err %d\n", err);
+               return err;
+       }
+       return 0;
+}
+
+void sspi_sig_xfer(bool xfer, struct cfspi_dev *dev)
+{
+       /* If xfer is true then you should assert the SPI_INT to indicate to
+        * the master that you are ready to recieve the data from the master
+        * SPI. If xfer is false then you should de-assert SPI_INT to indicate
+        * that the transfer is done.
+        */
+       if (xfer)
+               gpio_set_value(tegra_caif_sspi_gpio_spi_int, 1);
+       else
+               gpio_set_value(tegra_caif_sspi_gpio_spi_int, 0);
+}
+
+static void sspi_release(struct device *dev)
+{
+       /*
+        * Here you should release your SPI device resources.
+        */
+}
+
+static int __init sspi_init(void)
+{
+       /* Here you should initialize your SPI device by providing the
+        * necessary functions, clock speed, name and private data. Once
+        * done, you can register your device with the
+        * platform_device_register() function. This function will return
+        * with the CAIF SPI interface initialized. This is probably also
+        * the place where you should set up your GPIOs, interrupts and SPI
+        * resources.
+        */
+
+       int res = 0;
+
+       /* Register Tegra SPI protocol driver. */
+       res = spi_register_driver(&tegra_caif_spi_slave_driver);
+       if (res < 0)
+               return res;
+
+       /* Initialize slave device. */
+       slave.sdev.init_xfer = sspi_init_xfer;
+       slave.sdev.sig_xfer = sspi_sig_xfer;
+       slave.sdev.clk_mhz = SPI_MASTER_CLK_MHZ;
+       slave.sdev.priv = &slave;
+       slave.sdev.name = "spi_sspi";
+       slave_device.dev.release = sspi_release;
+
+       /* Initialize platform device. */
+       slave_device.name = "cfspi_sspi";
+       slave_device.dev.platform_data = &slave.sdev;
+
+       /* Register platform device. */
+       res = platform_device_register(&slave_device);
+       if (res)
+               return -ENODEV;
+
+       return res;
+}
+
+static void __exit sspi_exit(void)
+{
+       /* Delete platfrom device. */
+       platform_device_del(&slave_device);
+
+       /* Free Tegra SPI protocol driver. */
+       spi_unregister_driver(&tegra_caif_spi_slave_driver);
+
+       /* Free Tegra GPIO interrupts. */
+       disable_irq(gpio_to_irq(tegra_caif_sspi_gpio_spi_ss));
+       free_irq(gpio_to_irq(tegra_caif_sspi_gpio_spi_ss), &slave_device);
+
+       /* Free Tegra GPIOs. */
+       gpio_free(tegra_caif_sspi_gpio_spi_ss);
+       gpio_free(tegra_caif_sspi_gpio_spi_int);
+}
+
+static int __devinit tegra_caif_spi_slave_probe(struct spi_device *spi)
+{
+       struct tegra_caif_platform_data *pdata;
+       int res;
+
+       if (!spi)
+               return -ENODEV;
+
+       pdata = spi->dev.platform_data;
+       if (!pdata)
+               return -ENODEV;
+
+       tegra_caif_sspi_gpio_spi_int = pdata->spi_int;
+       tegra_caif_sspi_gpio_spi_ss = pdata->spi_ss;
+       tegra_caif_sspi_gpio_reset = pdata->reset;
+       tegra_caif_sspi_gpio_power = pdata->power;
+       tegra_caif_sspi_gpio_awr = pdata->awr;
+       tegra_caif_sspi_gpio_cwr = pdata->cwr;
+
+       tegra_caif_spi_slave_device = spi;
+
+       /* Initialize Tegra GPIOs. */
+       res = gpio_request(tegra_caif_sspi_gpio_spi_int, "caif_sspi_spi_int");
+       if (res < 0)
+               goto err1;
+
+       res = gpio_request(tegra_caif_sspi_gpio_spi_ss, "caif_sspi_ss");
+       if (res < 0)
+               goto err2;
+
+       res = gpio_direction_output(tegra_caif_sspi_gpio_spi_int, 0);
+       if (res < 0)
+               goto err3;
+
+       res = gpio_direction_input(tegra_caif_sspi_gpio_spi_ss);
+       if (res < 0)
+               goto err4;
+
+       tegra_caif_modem_power(1);
+       msleep(2000);
+
+       /* Initialize Tegra GPIO interrupts. */
+       res = request_irq(gpio_to_irq(tegra_caif_sspi_gpio_spi_ss),
+               sspi_irq, IRQF_TRIGGER_FALLING, "caif_sspi_ss_irq",
+               &slave.sdev);
+       if (res < 0)
+               goto err5;
+
+       return 0;
+err5:
+       free_irq(gpio_to_irq(tegra_caif_sspi_gpio_spi_ss), &slave_device);
+err4:
+err3:
+       gpio_free(tegra_caif_sspi_gpio_spi_ss);
+err2:
+       gpio_free(tegra_caif_sspi_gpio_spi_int);
+err1:
+       return res;
+}
+
+module_init(sspi_init);
+module_exit(sspi_exit);
diff --git a/include/linux/tegra_caif.h b/include/linux/tegra_caif.h
new file mode 100644 (file)
index 0000000..fed6749
--- /dev/null
@@ -0,0 +1,34 @@
+/* include/linux/tegra_caif.h
+ *
+ * Copyright (C) 2011 NVIDIA Corporation
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#ifndef _TEGRA_CAIF_H_
+#define _TEGRA_CAIF_H_
+
+/* The GPIO details needed by the rainbow caif */
+struct tegra_caif_platform_data {
+       int reset;
+       int power;
+       int awr;
+       int cwr;
+       int spi_int;
+       int spi_ss;
+};
+
+#endif /* _TEGRA_CAIF_H_ */
+