Driver : add new modem driver sc8800 & tdsc8800
author张昊 <zhanghao@rock-chips.com>
Wed, 23 Nov 2011 04:40:21 +0000 (12:40 +0800)
committer张昊 <zhanghao@rock-chips.com>
Wed, 23 Nov 2011 04:40:21 +0000 (12:40 +0800)
drivers/misc/Kconfig
drivers/misc/Makefile
drivers/misc/sc8800.c [new file with mode: 0755]
drivers/misc/tdsc8800.c [new file with mode: 0755]

index a9a94491b0284d9ddb847ac24b9e212a66c8f529..710153c51556985d22478dfc965f6b1eee287b39 100644 (file)
@@ -540,7 +540,8 @@ config STE
 
 config MTK23D
        bool "MTK6223D modem control driver"
-
+       default n       
+       
 config FM580X
        bool "FM rda580x driver"
 
@@ -553,6 +554,15 @@ config MW100
 config RK29_NEWTON
        bool "RK29_NEWTON misc driver"
 
+config RK29_SC8800
+       bool "SC8800 misc driver"
+       default n
+
+config TDSC8800
+       bool "TDSC8800 modem control driver"
+       default n
+       
+
 source "drivers/misc/c2port/Kconfig"
 source "drivers/misc/eeprom/Kconfig"
 source "drivers/misc/cb710/Kconfig"
index c4ed797a893bcfb486333c7146b5095d6e8a4bb5..3a67414ad02359a3fce9c5b408cb30bb365a9de1 100644 (file)
@@ -61,3 +61,5 @@ obj-$(CONFIG_RK29_SUPPORT_MODEM)      += rk29_modem/
 obj-$(CONFIG_GPS_GNS7560)      += gps/
 obj-y += mpu3050/
 obj-$(CONFIG_RK29_NEWTON)      += newton.o
+obj-$(CONFIG_TDSC8800) += tdsc8800.o
+obj-$(CONFIG_RK29_SC8800)      +=      sc8800.o
diff --git a/drivers/misc/sc8800.c b/drivers/misc/sc8800.c
new file mode 100755 (executable)
index 0000000..c8d26fe
--- /dev/null
@@ -0,0 +1,728 @@
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/spi/spi.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/miscdevice.h>
+#include <linux/wait.h>
+#include <linux/uaccess.h>
+#include <linux/fs.h>
+#include <linux/rwsem.h>
+#include <mach/gpio.h>
+#include <mach/iomux.h>
+#include <mach/board.h>
+#include <linux/wakelock.h>
+
+#if 0
+#define sc8800_dbg(dev, format, arg...)                \
+       dev_printk(KERN_INFO , dev , format , ## arg)
+//#define SC8800_PRINT_BUF
+#else
+#define sc8800_dbg(dev, format, arg...)
+#endif
+
+#define READ_TIMEOUT           30000  //no use
+#define WRITE_TIMEOUT          80  //80ms
+
+#define RD_BUF_SIZE                    (16*PAGE_SIZE) // 64K
+#define WR_BUF_SIZE                    (2*PAGE_SIZE) // __get_free_pages(GFP_KERNEL, 1) ==> pages = 2^1 = 2
+#define MAX_RX_LIST                    256
+
+#define BP_PACKET_SIZE         64
+#define BP_PACKET_HEAD_LEN  16
+#define BP_PACKET_DATA_LEN  48
+
+struct plat_sc8800 {
+       int slav_rts_pin;
+       int slav_rdy_pin;
+       int master_rts_pin;
+       int master_rdy_pin;
+       int poll_time;
+};
+struct bp_head{
+       u16 tag;        //0x7e7f
+       u16 type;       //0xaa55
+       u32 length;     //the length of data after head(8192-128 bytes) 
+       u32 fram_num;   //no used , always 0
+       u32 reserved;   ////reserved 
+       char data[BP_PACKET_DATA_LEN];
+};
+
+struct sc8800_data {
+       struct device                   *dev;
+       struct spi_device               *spi;
+       struct workqueue_struct *rx_wq;
+       struct workqueue_struct *tx_wq;
+       struct work_struct              rx_work;
+       struct work_struct              tx_work;
+       struct wake_lock        rx_wake;
+       struct wake_lock  tx_wake;
+
+       wait_queue_head_t               waitrq;
+       wait_queue_head_t               waitwq;
+       spinlock_t                              lock;
+
+       int irq;
+       int slav_rts;
+       int slav_rdy;
+       int master_rts;
+       int master_rdy;
+
+       int write_finished;
+       int write_tmo;
+
+       char *rx_buf;
+       int     rx_len; 
+
+       char *tx_buf;
+       int     tx_len; 
+
+       int rw_enable;
+
+       int is_suspend;
+};
+static DEFINE_MUTEX(sc8800s_lock);
+static DECLARE_RWSEM(sc8800_rsem);  
+static DECLARE_RWSEM(sc8800_wsem);  
+struct sc8800_data *g_sc8800 = NULL;
+
+char tmp_buf[1024];
+static int bp_rts(struct sc8800_data *sc8800)
+{
+       return gpio_get_value(sc8800->slav_rts);
+}
+
+static int bp_rdy(struct sc8800_data *sc8800)
+{
+       return gpio_get_value(sc8800->slav_rdy);
+}
+
+static void ap_rts(struct sc8800_data *sc8800, int value)
+{
+       gpio_set_value(sc8800->master_rts, value);
+}
+
+static void ap_rdy(struct sc8800_data *sc8800, int value)
+{
+       gpio_set_value(sc8800->master_rdy, value);
+}
+static void sc8800_print_buf(struct sc8800_data *sc8800, char *buf, const char *func, int len)
+{
+#ifdef SC8800_PRINT_BUF
+       int i;
+       char *tmp = NULL;
+       tmp = kzalloc(len*7, GFP_KERNEL);
+       sc8800_dbg(sc8800->dev, "%s buf[%d] = :\n", func, len);
+       for(i = 0; i < len; i++){
+               if(i % 16 == 0 && i != 0)
+                       sprintf(tmp, "%s\n", tmp);
+               sprintf(tmp, "%s[0x%2x] ", tmp, buf[i]);
+       }
+       printk("%s\n", tmp);
+       kfree(tmp);
+#endif
+       return;
+}
+static void buf_swp(char *buf, int len)
+{
+       int i;
+       char temp;
+               
+       len = (len/2)*2;
+       for(i = 0; i < len; i += 2)
+       {
+               temp = buf[i];
+               buf[i] = buf[i+1];
+               buf[i+1] = temp;
+       }
+}
+static void spi_in(struct sc8800_data *sc8800, char *tx_buf, unsigned len, int* err)
+{
+       struct spi_message message;
+       struct spi_transfer tran;
+
+       buf_swp(tx_buf, len);
+
+       tran.tx_buf = (void *)tx_buf;
+       tran.rx_buf = tmp_buf;
+       tran.len = len;
+       tran.speed_hz = 0;
+       tran.bits_per_word = 16;
+
+       spi_message_init(&message);
+       spi_message_add_tail(&tran, &message);
+       *err = spi_sync(sc8800->spi, &message);
+       sc8800_print_buf(sc8800, tx_buf, __func__, len);
+}
+
+static void spi_out(struct sc8800_data *sc8800, char *rx_buf, unsigned len, int* err)
+{
+       struct spi_message message;
+       struct spi_transfer tran;
+
+       memset(rx_buf, 0, len);
+       tran.tx_buf = tmp_buf;
+       tran.rx_buf = (void *)rx_buf;
+       tran.len = len;
+       tran.speed_hz = 0;
+       tran.bits_per_word = 16;
+
+       spi_message_init(&message);
+       spi_message_add_tail(&tran, &message);
+       *err = spi_sync(sc8800->spi, &message);
+       sc8800_print_buf(sc8800, rx_buf, __func__, len);
+
+       buf_swp(rx_buf, len);
+}
+
+static int ap_get_head(struct sc8800_data *sc8800, struct bp_head *packet)
+{
+       int err = 0, count = 5;
+       char buf[BP_PACKET_SIZE];
+
+retry:
+       spi_out(sc8800, buf, BP_PACKET_SIZE, &err);
+
+       if(err < 0 && count > 0)
+       {
+               dev_warn(sc8800->dev, "%s spi_out return error, retry count = %d\n",
+                               __func__, count);
+               count--;
+               mdelay(10);
+               goto retry;
+       }
+       if(err < 0)
+               return err;
+
+       memcpy((char *)(packet), buf, BP_PACKET_SIZE);
+
+       sc8800_dbg(sc8800->dev, "%s tag = 0x%4x, type = 0x%4x, length = %x\n",
+                       __func__, packet->tag, packet->type, packet->length);
+               
+       if ((packet->tag != 0x7e7f) || (packet->type != 0xaa55)) 
+               return -1;
+       else
+               return 0;
+}
+
+
+static int sc8800_rx(struct sc8800_data *sc8800)
+{
+       int ret = 0, len, real_len;
+       struct bp_head packet;
+       char *buf = NULL;
+
+       ap_rdy(sc8800,0);
+       ret = ap_get_head(sc8800, &packet);
+
+       if(ret < 0){
+               dev_err(sc8800->dev, "ERR: %s ap_get_head err = %d\n", __func__, ret);
+               goto out;
+       }
+       len = packet.length;
+       if(len > BP_PACKET_DATA_LEN)
+               real_len =      (((len -BP_PACKET_DATA_LEN-1)/BP_PACKET_SIZE)+2)*BP_PACKET_SIZE;
+       else
+               real_len = BP_PACKET_SIZE;
+       if(len > RD_BUF_SIZE){
+               dev_err(sc8800->dev, "ERR: %s len[%d] is large than buffer size[%lu]\n",
+                               __func__, real_len, RD_BUF_SIZE);       
+               goto out;
+       }
+       buf = kzalloc(real_len, GFP_KERNEL);
+       if(!buf){
+               dev_err(sc8800->dev,"ERR: %s no memmory for rx_buf\n", __func__);
+               goto out;
+       }
+
+       memcpy(buf, packet.data, BP_PACKET_DATA_LEN);
+       if(len > BP_PACKET_DATA_LEN)
+               spi_out(sc8800, buf + BP_PACKET_DATA_LEN, real_len-BP_PACKET_SIZE, &ret);
+       
+       if(ret < 0){
+               dev_err(sc8800->dev, "ERR: %s spi out err = %d\n", __func__, ret);
+               goto out;
+       }
+
+       spin_lock(&sc8800->lock);
+       if(sc8800->rx_len + len > RD_BUF_SIZE){
+               dev_warn(sc8800->dev, "WARN: %s read buffer is full\n", __func__);
+       }else
+       {
+               memcpy(sc8800->rx_buf+sc8800->rx_len, buf, len);
+               sc8800->rx_len += len;
+       }
+       spin_unlock(&sc8800->lock);
+
+       sc8800_dbg(sc8800->dev, "%s rx_len = %d\n", __func__, sc8800->rx_len);
+
+       ret = sc8800->rx_len;
+
+out:
+       ap_rdy(sc8800,1);
+       if(buf)
+               kfree(buf);
+       buf = NULL;
+       return ret;
+
+}
+ static int sc8800_data_packet(char *dst, char *src, int src_len)
+{
+       int dst_len = 0;
+       struct bp_head packet;
+
+       if(src_len > BP_PACKET_DATA_LEN)
+               dst_len = (((src_len -BP_PACKET_DATA_LEN-1)/BP_PACKET_SIZE)+2)*BP_PACKET_SIZE;
+       else
+               dst_len = BP_PACKET_SIZE;
+       
+       packet.tag =0x7e7f;
+       packet.type = 0xaa55;
+       packet.length = src_len;
+       packet.fram_num = 0;
+       packet.reserved = 0;
+       
+       memcpy(packet.data, src, BP_PACKET_DATA_LEN);   
+       memcpy(dst, (char *)&packet, BP_PACKET_SIZE);
+       if(src_len >= BP_PACKET_DATA_LEN)
+               memcpy(dst+BP_PACKET_SIZE, src+BP_PACKET_DATA_LEN, src_len - BP_PACKET_DATA_LEN);
+
+       return dst_len;
+ }
+
+static int sc8800_tx(struct sc8800_data *sc8800)
+{
+       int ret = 0, len;
+
+       char *buf = NULL;
+
+       sc8800->write_tmo = 0;
+       buf = kzalloc(WR_BUF_SIZE + BP_PACKET_SIZE, GFP_KERNEL);
+       if(!buf){
+               dev_err(sc8800->dev, "ERR: no memery for buf\n");
+               return -ENOMEM;
+       }
+       while(!bp_rts(sc8800)){
+               if(sc8800->write_tmo){
+                       sc8800_dbg(sc8800->dev, "bp_rts = 0\n");
+                       kfree(buf);
+                       return -1;
+               }
+               schedule();
+       }
+       mutex_lock(&sc8800s_lock);
+       ap_rts(sc8800,0);
+#if 1
+       while(bp_rdy(sc8800)){
+               if(sc8800->write_tmo){
+                       ap_rts(sc8800,1);
+                       sc8800_dbg(sc8800->dev, "ERR: %s write timeout ->bp not ready (bp_rdy = 1)\n", __func__);
+                       kfree(buf);
+                       mutex_unlock(&sc8800s_lock);
+                       return -1;
+               }
+               schedule();
+       }
+#endif
+       len = sc8800_data_packet(buf, sc8800->tx_buf, sc8800->tx_len);
+       spi_in(sc8800, buf, len, &ret);
+
+       if(ret < 0)
+               dev_err(sc8800->dev, "ERR: %s spi in err = %d\n", __func__, ret);
+       ap_rts(sc8800,1);
+       if(buf){
+               kfree(buf);
+               buf = NULL;
+       }
+
+       mutex_unlock(&sc8800s_lock);
+#if 1  
+       while(!bp_rdy(sc8800)){
+               if(sc8800->write_tmo){
+                       dev_err(sc8800->dev, "ERR: %s write timeout -> bp receiving (bp_rdy = 0)\n", __func__);
+                       ret = -1;
+               }
+               schedule();
+       }
+#endif
+       return ret;
+}
+
+static irqreturn_t sc8800_irq(int irq, void *dev_id)
+{
+       struct sc8800_data *sc8800 = (struct sc8800_data *)dev_id;
+       
+       sc8800_dbg(sc8800->dev, "%s\n", __func__);
+#if 0
+       if(sc8800->is_suspend)
+               rk28_send_wakeup_key();
+#endif
+       wake_lock(&sc8800->rx_wake);
+       queue_work(sc8800->rx_wq, &sc8800->rx_work);
+       return IRQ_HANDLED;
+}
+
+static void sc8800_rx_work(struct work_struct *rx_work)
+{
+       struct sc8800_data *sc8800 = container_of(rx_work, struct sc8800_data, rx_work);
+       
+       sc8800_dbg(sc8800->dev, "%s\n", __func__);
+       mutex_lock(&sc8800s_lock);
+       sc8800->rx_len = sc8800_rx(sc8800);
+       wake_unlock(&sc8800->rx_wake);
+       if(sc8800->rx_len <= 0)
+               sc8800->rx_len = 0;
+       wake_up(&sc8800->waitrq);
+       mutex_unlock(&sc8800s_lock);
+}
+static void sc8800_tx_work(struct work_struct *tx_work)
+{
+       struct sc8800_data *sc8800 = container_of(tx_work, struct sc8800_data, tx_work);
+
+       sc8800_dbg(sc8800->dev, "%s bp_rts = %d\n", __func__, bp_rts(sc8800));
+       wake_lock(&sc8800->tx_wake);
+       if(sc8800_tx(sc8800) == 0){
+               sc8800->write_finished = 1;
+               wake_up(&sc8800->waitwq);
+       }
+       wake_unlock(&sc8800->tx_wake);
+}
+static ssize_t sc8800_read(struct file *file,
+                       char __user *buf, size_t count, loff_t *offset)
+{
+       int ret = 0;
+       ssize_t size = 0;
+       struct sc8800_data *sc8800 = (struct sc8800_data *)file->private_data;
+
+       sc8800_dbg(sc8800->dev, "%s count = %d\n", __func__, count);
+       if(!buf){
+               dev_err(sc8800->dev, "ERR: %s user_buf = NULL\n", __func__);
+               return -EFAULT;
+       }
+       down_write(&sc8800_rsem);
+       if(!(file->f_flags & O_NONBLOCK)){
+               ret = wait_event_interruptible(sc8800->waitrq, (sc8800->rx_len > 0 || (sc8800->rw_enable <= 0)));
+               if (ret) {
+                       up_write(&sc8800_rsem);
+                       return ret;
+               }
+       }
+       if(sc8800->rw_enable <= 0){
+               dev_err(sc8800->dev, "ERR: %s sc8800 is released\n", __func__);
+               up_write(&sc8800_rsem);
+               return -EFAULT;
+       }
+       if(sc8800->rx_len == 0){
+               dev_warn(sc8800->dev, "WARN: %s nonblock read, rx_len = 0\n", __func__);
+               return 0;
+       }
+       spin_lock(&sc8800->lock);
+       sc8800_print_buf(sc8800, sc8800->rx_buf, __func__, sc8800->rx_len);
+       if(sc8800->rx_len > count){
+               size = count;
+               ret = copy_to_user(buf, sc8800->rx_buf, count);
+       }else{
+               size = sc8800->rx_len;
+               ret = copy_to_user(buf, sc8800->rx_buf, sc8800->rx_len);
+       }
+
+       if(ret < 0){
+               dev_err(sc8800->dev, "ERR: %s copy to user ret = %d\n", __func__, ret);
+               spin_unlock(&sc8800->lock);
+               up_write(&sc8800_rsem);
+               return -EFAULT;
+       }
+       if(sc8800->rx_len > count)
+               memmove(sc8800->rx_buf, sc8800->rx_buf + count, sc8800->rx_len - count);
+       
+       sc8800->rx_len -= size;
+       spin_unlock(&sc8800->lock);
+       up_write(&sc8800_rsem);
+       return size;
+}
+static ssize_t sc8800_write(struct file *file, 
+                       const char __user *buf, size_t count, loff_t *offset)
+{
+       int ret = 0;
+       struct sc8800_data *sc8800 = (struct sc8800_data *)file->private_data;
+
+       sc8800_dbg(sc8800->dev, "%s count = %d\n", __func__, count);
+       if(count > WR_BUF_SIZE){
+               dev_err(sc8800->dev, "ERR: %s count[%u] > WR_BUF_SIZE[%lu]\n",
+                               __func__, count, WR_BUF_SIZE);
+               return -EFAULT; 
+       }
+       down_write(&sc8800_wsem);
+       sc8800_print_buf(sc8800, sc8800->tx_buf, __func__, count);
+       memset(sc8800->tx_buf, 0, WR_BUF_SIZE);
+       ret = copy_from_user(sc8800->tx_buf, buf, count);
+       if(ret < 0){
+               dev_err(sc8800->dev, "ERR: %s copy from user ret = %d\n", __func__, ret);
+               up_write(&sc8800_wsem);
+               return -EFAULT;
+       }
+
+       sc8800->write_finished = 0;
+       sc8800->tx_len = count;
+       queue_work(sc8800->tx_wq, &sc8800->tx_work);
+
+       ret = wait_event_timeout(sc8800->waitwq,
+                               (sc8800->write_finished || sc8800->rw_enable <= 0),
+                               msecs_to_jiffies(WRITE_TIMEOUT));
+       if(sc8800->rw_enable <= 0){
+               dev_err(sc8800->dev, "ERR: %ssc8800 is released\n", __func__);
+               up_write(&sc8800_wsem);
+               return  -EFAULT;
+       }
+       if(ret == 0){
+               sc8800->write_tmo = 1;
+               dev_err(sc8800->dev, "ERR: %swrite timeout\n", __func__);
+               up_write(&sc8800_wsem);
+               return -ETIMEDOUT;
+               //return count;
+       }else{
+               up_write(&sc8800_wsem);
+               return count;
+       }
+}
+
+static int sc8800_open(struct inode *inode, struct file *file)
+{
+       file->private_data = g_sc8800;
+       
+       sc8800_dbg(g_sc8800->dev, "%s\n", __func__);
+       g_sc8800->write_finished = 0;
+       g_sc8800->write_tmo = 0;
+       g_sc8800->rw_enable++;
+       return 0;
+}
+static int sc8800_release(struct inode *inode, struct file *file)
+{
+       struct sc8800_data *sc8800 = (struct sc8800_data *)file->private_data;
+
+       sc8800_dbg(sc8800->dev, "%s\n", __func__);
+       if(sc8800->rw_enable > 0)
+               sc8800->rw_enable--;
+       wake_up(&sc8800->waitrq);
+       wake_up(&sc8800->waitwq);
+
+       return 0;
+}
+static const struct file_operations sc8800_fops = {
+       .open = sc8800_open,
+       .release = sc8800_release,
+       .read = sc8800_read,
+       .write = sc8800_write,
+};
+static struct miscdevice sc8800_device = {
+       .minor = MISC_DYNAMIC_MINOR,
+       .name = "sc8800",
+       .fops = &sc8800_fops,
+};
+
+static int __devinit sc8800_probe(struct spi_device *spi)
+{
+       int ret = 0;
+       unsigned long page;
+       struct sc8800_data *sc8800 = NULL;
+       struct plat_sc8800 *pdata = NULL;
+
+       pdata = spi->dev.platform_data;
+       if (!pdata) {
+               dev_err(&spi->dev, "ERR: spi data missing\n");
+               return -ENODEV;
+       }
+
+       sc8800 = (struct sc8800_data *)kzalloc(sizeof(struct sc8800_data), GFP_KERNEL);
+       if(!sc8800){
+               dev_err(&spi->dev, "ERR: no memory for sc8800\n");
+               return -ENOMEM;
+       }
+       g_sc8800 = sc8800;
+
+       page = __get_free_pages(GFP_KERNEL, 4); //2^4 * 4K = 64K
+       if(!page){
+               dev_err(&spi->dev, "ERR: no memory for rx_buf\n");
+               ret = -ENOMEM;
+               goto err_get_free_page1;
+       }
+       sc8800->rx_buf = (char *)page;
+
+       page = __get_free_pages(GFP_KERNEL, 1);//2^1 * 4K = 8K
+       if(!page){
+               dev_err(&spi->dev, "ERR: no memory for tx_buf\n");
+               ret = -ENOMEM;
+               goto err_get_free_page2;
+       }
+       sc8800->tx_buf = (char *)page;
+
+       sc8800->spi = spi;
+       sc8800->dev = &spi->dev;
+       dev_set_drvdata(sc8800->dev, sc8800);
+
+       spi->bits_per_word = 16;
+       spi->mode = SPI_MODE_2;
+       spi->max_speed_hz = 1000*1000*8;
+       ret = spi_setup(spi);
+       if (ret < 0){
+               dev_err(sc8800->dev, "ERR: fail to setup spi\n");
+               goto err_spi_setup;
+       }
+       sc8800->irq = gpio_to_irq(pdata->slav_rts_pin);
+       sc8800->slav_rts = pdata->slav_rts_pin;
+       sc8800->slav_rdy = pdata->slav_rdy_pin;
+       sc8800->master_rts = pdata->master_rts_pin;
+       sc8800->master_rdy = pdata->master_rdy_pin;
+
+        rk29_mux_api_set(GPIO1C1_UART0RTSN_SDMMC1WRITEPRT_NAME, GPIO1H_GPIO1C1);
+        rk29_mux_api_set(GPIO1C0_UART0CTSN_SDMMC1DETECTN_NAME, GPIO1H_GPIO1C0);
+
+       ret = gpio_request(sc8800->slav_rts, "salv_rts");
+       if(ret < 0){
+               dev_err(sc8800->dev, "ERR: gpio request slav_rts[%d]\n", sc8800->slav_rts);
+               goto err_gpio_request_slav_rts;
+       }
+       ret = gpio_request(sc8800->slav_rdy, "slav_rdy");
+       if(ret < 0){
+               dev_err(sc8800->dev, "ERR: gpio request slav_rdy\n");
+               goto err_gpio_request_slav_rdy;
+       }
+       ret = gpio_request(sc8800->master_rts, "master_rts");
+       if(ret < 0){
+               dev_err(sc8800->dev, "ERR: gpio request master_rts\n");
+               goto err_gpio_request_master_rts;
+       }
+       ret = gpio_request(sc8800->master_rdy, "master_rdy");
+       if(ret < 0){
+               dev_err(sc8800->dev, "ERR: gpio request master_rdy\n");
+               goto err_gpio_request_master_rdy;
+       }
+       gpio_direction_input(sc8800->slav_rts);
+       gpio_pull_updown(sc8800->slav_rts, GPIOPullUp);
+       gpio_direction_input(sc8800->slav_rdy);
+       gpio_pull_updown(sc8800->slav_rdy, GPIOPullUp);
+       gpio_direction_output(sc8800->master_rts, GPIO_HIGH);
+       gpio_direction_output(sc8800->master_rdy, GPIO_HIGH);
+
+       
+       init_waitqueue_head(&sc8800->waitrq);
+       init_waitqueue_head(&sc8800->waitwq);
+       spin_lock_init(&sc8800->lock);
+
+       ret = misc_register(&sc8800_device);
+       if(ret < 0){
+       
+               dev_err(sc8800->dev, "ERR: fail to register misc device\n");
+               goto err_misc_register;
+       }
+       ret = request_irq(sc8800->irq, sc8800_irq, IRQF_TRIGGER_FALLING, "sc8800", sc8800);
+       if(ret < 0){
+               dev_err(sc8800->dev, "ERR: fail to request irq %d\n", sc8800->irq);
+               goto err_request_irq;
+       }
+       wake_lock_init(&sc8800->rx_wake, WAKE_LOCK_SUSPEND, "sc8800_rx_wake");
+       wake_lock_init(&sc8800->tx_wake, WAKE_LOCK_SUSPEND, "sc8800_tx_wake");
+       enable_irq_wake(sc8800->irq);
+       sc8800->rx_wq = create_workqueue("sc8800_rxwq"); 
+       sc8800->tx_wq = create_workqueue("sc8800_txwq"); 
+       INIT_WORK(&sc8800->rx_work, sc8800_rx_work);
+       INIT_WORK(&sc8800->tx_work, sc8800_tx_work);
+       dev_info(sc8800->dev, "sc8800 probe ok\n");
+       return 0;
+
+err_request_irq:
+       gpio_free(sc8800->master_rdy);
+err_misc_register:
+       misc_deregister(&sc8800_device);
+err_gpio_request_master_rdy:
+       gpio_free(sc8800->master_rts);
+err_gpio_request_master_rts:
+       gpio_free(sc8800->slav_rdy);
+err_gpio_request_slav_rdy:
+       gpio_free(sc8800->slav_rts);
+err_gpio_request_slav_rts:
+err_spi_setup:
+       free_page((unsigned long)sc8800->tx_buf);
+err_get_free_page2:
+       free_page((unsigned long)sc8800->rx_buf);
+err_get_free_page1:
+       kfree(sc8800);
+       sc8800 = NULL;
+       g_sc8800 = NULL;
+       return ret;
+}
+
+static int __devexit sc8800_remove(struct spi_device *spi)
+{
+       struct sc8800_data *sc8800 = dev_get_drvdata(&spi->dev);
+
+       destroy_workqueue(sc8800->rx_wq); 
+       destroy_workqueue(sc8800->tx_wq); 
+       free_irq(sc8800->irq, sc8800);
+       gpio_free(sc8800->master_rdy);
+       gpio_free(sc8800->master_rts);
+       gpio_free(sc8800->slav_rdy);
+       gpio_free(sc8800->slav_rts);
+       free_page((unsigned long)sc8800->tx_buf);
+       free_page((unsigned long)sc8800->rx_buf);
+       kfree(sc8800);
+       sc8800 = NULL;
+       return 0;
+}
+
+#ifdef CONFIG_PM
+
+static int sc8800_suspend(struct spi_device *spi, pm_message_t state)
+{
+       struct sc8800_data *sc8800 = dev_get_drvdata(&spi->dev);
+
+       sc8800->is_suspend = 1; 
+       return 0;
+}
+
+static int sc8800_resume(struct spi_device *spi)
+{
+       struct sc8800_data *sc8800 = dev_get_drvdata(&spi->dev);
+
+
+       sc8800->is_suspend = 0; 
+       return 0;
+}
+
+#else
+#define sc8800_suspend NULL
+#define sc8800_resume  NULL
+#endif
+
+static struct spi_driver sc8800_driver = {
+       .driver = {
+               .name           = "sc8800",
+               .bus            = &spi_bus_type,
+               .owner          = THIS_MODULE,
+       },
+
+       .probe          = sc8800_probe,
+       .remove         = __devexit_p(sc8800_remove),
+       .suspend        = sc8800_suspend,
+       .resume         = sc8800_resume,
+};
+
+static int __init sc8800_init(void)
+{
+       printk("sc8800_init\n");
+       return spi_register_driver(&sc8800_driver);
+}
+module_init(sc8800_init);
+
+static void __exit sc8800_exit(void)
+{
+       spi_unregister_driver(&sc8800_driver);
+}
+module_exit(sc8800_exit);
+
+MODULE_DESCRIPTION("SC8800 driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("spi:SC8800");
diff --git a/drivers/misc/tdsc8800.c b/drivers/misc/tdsc8800.c
new file mode 100755 (executable)
index 0000000..d4ffb89
--- /dev/null
@@ -0,0 +1,186 @@
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/i2c.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/input.h>
+#include <linux/platform_device.h>
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+#include <linux/miscdevice.h>
+#include <linux/circ_buf.h>
+#include <linux/interrupt.h>
+#include <linux/miscdevice.h>
+#include <mach/iomux.h>
+#include <mach/gpio.h>
+#include <linux/delay.h>
+#include <linux/poll.h>
+#include <linux/wait.h>
+#include <linux/slab.h>
+
+#include <linux/workqueue.h>
+#include <linux/mtk23d.h>
+
+
+MODULE_LICENSE("GPL");
+
+#define DEBUG
+#ifdef DEBUG
+#define MODEMDBG(x...) printk(x)
+#else
+#define MODEMDBG(fmt,argss...)
+#endif
+
+static bool wakelock_inited;
+#define SLEEP 1
+#define READY 0
+struct rk2818_23d_data *gpdata = NULL;
+
+
+int modem_poweron_off(int on_off)
+{
+       struct rk2818_23d_data *pdata = gpdata;
+       int result, error = 0, irq = 0; 
+       
+       if(on_off)
+       {
+               printk("tdsc8800_poweron\n");
+               gpio_set_value(pdata->bp_power, pdata->bp_power_active_low? GPIO_HIGH:GPIO_LOW);  // power on enable
+
+       }
+       else
+       {
+               printk("tdsc8800_poweroff\n");
+               gpio_set_value(pdata->bp_power, pdata->bp_power_active_low? GPIO_LOW:GPIO_HIGH);
+       }
+}
+static int tdsc8800_open(struct inode *inode, struct file *file)
+{
+       struct rk2818_23d_data *pdata = gpdata;
+       //struct rk2818_23d_data *pdata = gpdata = pdev->dev.platform_data;
+       struct platform_data *pdev = container_of(pdata, struct device, platform_data);
+
+       MODEMDBG("tdsc8800_open\n");
+
+       int ret = 0;
+       modem_poweron_off(1);
+       device_init_wakeup(&pdev, 1);
+
+       return 0;
+}
+
+static int tdsc8800_release(struct inode *inode, struct file *file)
+{
+       MODEMDBG("tdsc8800_release\n");
+
+       return 0;
+}
+static int tdsc8800_ioctl(struct inode *inode,struct file *file, unsigned int cmd, unsigned long arg)
+{
+       return 0;
+}
+
+static struct file_operations tdsc8800_fops = {
+       .owner = THIS_MODULE,
+       .open =tdsc8800_open,
+       .release =tdsc8800_release,
+       .unlocked_ioctl = tdsc8800_ioctl
+};
+
+static struct miscdevice tdsc8800_misc = {
+       .minor = MISC_DYNAMIC_MINOR,
+       .name = "tdsc8800",
+       .fops = &tdsc8800_fops
+};
+
+static int tdsc8800_probe(struct platform_device *pdev)
+{
+       struct rk2818_23d_data *pdata = gpdata = pdev->dev.platform_data;
+       struct modem_dev *tdsc8800_data = NULL;
+       int result, error = 0, irq = 0; 
+       
+       MODEMDBG("tdsc8800_probe\n");
+
+       //pdata->io_init();
+
+       tdsc8800_data = kzalloc(sizeof(struct modem_dev), GFP_KERNEL);
+       if(NULL == tdsc8800_data)
+       {
+               printk("failed to request tdsc8800_data\n");
+               goto err6;
+       }
+       platform_set_drvdata(pdev, tdsc8800_data);
+
+       result = gpio_request(pdata->bp_power, "tdsc8800");
+       if (result) {
+               printk("failed to request BP_POW_EN gpio\n");
+               goto err1;
+       }
+       
+       
+        gpio_direction_output(pdata->bp_power, GPIO_LOW);
+
+       gpio_set_value(pdata->bp_power, pdata->bp_reset_active_low? GPIO_LOW:GPIO_HIGH);
+       result = misc_register(&tdsc8800_misc);
+       if(result)
+       {
+               MODEMDBG("misc_register err\n");
+       }
+       MODEMDBG("mtk23d_probe ok\n");
+       
+       return result;
+err1:
+       gpio_free(pdata->bp_power);
+err6:
+       kfree(tdsc8800_data);
+ret:
+       return result;
+}
+
+int tdsc8800_suspend(struct platform_device *pdev)
+{
+       return 0;
+}
+
+int tdsc8800_resume(struct platform_device *pdev)
+{
+       return 0;
+}
+
+void tdsc8800_shutdown(struct platform_device *pdev, pm_message_t state)
+{
+       struct rk2818_23d_data *pdata = pdev->dev.platform_data;
+       struct modem_dev *mt6223d_data = platform_get_drvdata(pdev);
+       
+       MODEMDBG("%s \n", __FUNCTION__);
+
+       modem_poweron_off(0);  // power down
+       gpio_free(pdata->bp_power);
+       kfree(mt6223d_data);
+}
+
+static struct platform_driver tdsc8800_driver = {
+       .probe  = tdsc8800_probe,
+       .shutdown       = tdsc8800_shutdown,
+       .suspend        = tdsc8800_suspend,
+       .resume         = tdsc8800_resume,
+       .driver = {
+               .name   = "tdsc8800",
+               .owner  = THIS_MODULE,
+       },
+};
+
+static int __init tdsc8800_init(void)
+{
+       MODEMDBG("tdsc8800_init ret=%d\n");
+       return platform_driver_register(&tdsc8800_driver);
+}
+
+static void __exit tdsc8800_exit(void)
+{
+       MODEMDBG("tdsc8800_exit\n");
+       platform_driver_unregister(&tdsc8800_driver);
+}
+
+module_init(tdsc8800_init);
+module_exit(tdsc8800_exit);