--- /dev/null
+#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");
--- /dev/null
+#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);