diff options
Diffstat (limited to 'drivers/char/modem_interface')
| -rw-r--r-- | drivers/char/modem_interface/Makefile | 15 | ||||
| -rw-r--r-- | drivers/char/modem_interface/boot_driver.c | 242 | ||||
| -rw-r--r-- | drivers/char/modem_interface/boot_protocol.c | 417 | ||||
| -rw-r--r-- | drivers/char/modem_interface/modem_buffer.c | 256 | ||||
| -rw-r--r-- | drivers/char/modem_interface/modem_buffer.h | 58 | ||||
| -rw-r--r-- | drivers/char/modem_interface/modem_gpio_drv.c | 358 | ||||
| -rw-r--r-- | drivers/char/modem_interface/modem_interface_driver.c | 1088 | ||||
| -rw-r--r-- | drivers/char/modem_interface/modem_interface_driver.h | 84 | ||||
| -rw-r--r-- | drivers/char/modem_interface/modem_sdio_drv.c | 50 | ||||
| -rw-r--r-- | drivers/char/modem_interface/modem_spi_drv.c | 124 | ||||
| -rw-r--r-- | drivers/char/modem_interface/normal_mode.c | 157 |
11 files changed, 2849 insertions, 0 deletions
diff --git a/drivers/char/modem_interface/Makefile b/drivers/char/modem_interface/Makefile new file mode 100644 index 00000000..e2a21bdd --- /dev/null +++ b/drivers/char/modem_interface/Makefile @@ -0,0 +1,15 @@ +# +# Makefile for DWC_otg Highspeed USB controller driver +# + +#PWD := $(shell pwd) +#PORTLIB := $(PWD)/../dwc_common_port + +ifeq ($(CONFIG_MODEM_INTF_DEBUG),y) + EXTRA_CFLAGS += -DDEBUG +endif + +obj-$(CONFIG_MODEM_INTF) += modem_interface.o +modem_interface-objs := boot_driver.o boot_protocol.o modem_buffer.o +modem_interface-objs += normal_mode.o modem_gpio_drv.o +modem_interface-objs += modem_sdio_drv.o modem_interface_driver.o diff --git a/drivers/char/modem_interface/boot_driver.c b/drivers/char/modem_interface/boot_driver.c new file mode 100644 index 00000000..4aac1ed9 --- /dev/null +++ b/drivers/char/modem_interface/boot_driver.c @@ -0,0 +1,242 @@ +/* linux/arch/arm/mach-sc8800g/boot_driver.c + * + * Common setup code for boot up MODEM + * + * Copyright (C) 2012 Spreadtrum + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#include <linux/module.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/device.h> +#include <linux/miscdevice.h> +#include <linux/string.h> +#include <linux/major.h> +#include <linux/init.h> +#include <linux/kdev_t.h> +#include <asm/uaccess.h> +#include <mach/modem_interface.h> +#include <linux/fs.h> +#include <linux/proc_fs.h> +#include <linux/timer.h> +#include <linux/mmc/sdio_channel.h> +#include <linux/wakelock.h> +#include <linux/slab.h> +#include <linux/delay.h> + + +#define DLOADER_NAME "dloader" +#define READ_BUFFER_SIZE (4096) +#define WRITE_BUFFER_SIZE (33*1024) + +extern void sdio_ipc_enable(u8 is_enable); +extern int modem_intf_send_bootcomp_message(); +struct dloader_dev{ + enum MODEM_Mode_type mode; + int open_count; + atomic_t read_excl; + atomic_t write_excl; + char *read_buffer; + char *write_buffer; +}; +static struct dloader_dev *dl_dev=NULL; +static struct wake_lock dloader_wake_lock; +static inline int dloader_lock(atomic_t *excl) +{ + if (atomic_inc_return(excl) == 1) { + return 0; + } else { + atomic_dec(excl); + return -1; + } +} + +static inline void dloader_unlock(atomic_t *excl) +{ + atomic_dec(excl); +} + +static int dloader_open(struct inode *inode,struct file *filp) +{ + int rval; + if (dl_dev==NULL) { + return -EIO; + } + if (dl_dev->open_count!=0) { + printk(KERN_INFO "dloader_open %d \n",dl_dev->open_count); + return -EBUSY; + } + + dl_dev->read_buffer = kzalloc(READ_BUFFER_SIZE, GFP_KERNEL); + if (dl_dev->read_buffer == NULL) { + printk(KERN_INFO "DLoade_open fail(NO MEM) \n"); + return -ENOMEM; + } + dl_dev->write_buffer = kzalloc(WRITE_BUFFER_SIZE+4, GFP_KERNEL); + if (dl_dev->write_buffer == NULL) { + kfree(dl_dev->read_buffer); + dl_dev->read_buffer = NULL; + printk(KERN_INFO "DLoade_open fail(NO MEM) \n"); + return -ENOMEM; + } + + if(modem_intf_open(MODEM_MODE_BOOT,0)< 0){ + kfree(dl_dev->write_buffer); + dl_dev->write_buffer = NULL; + kfree(dl_dev->read_buffer); + dl_dev->read_buffer = NULL; + printk(KERN_INFO "modem_intf_open failed \n"); + return -EBUSY; + } + + dl_dev->open_count++; + wake_lock(&dloader_wake_lock); + + dl_dev->mode = MODEM_MODE_BOOT; + + printk(KERN_INFO "DLoader_open %d times: %d\n", dl_dev->open_count, rval); + return 0; +} +static int dloader_read(struct file *filp,char __user *buf,size_t count,loff_t *pos) +{ + int read_len=0; + + if ((dl_dev==NULL)||(dl_dev->open_count!=1)) { + return -EIO; + } + + if (dloader_lock(&dl_dev->read_excl)) + return -EBUSY; + + if(dl_dev->mode==MODEM_MODE_BOOT){ + if(count > READ_BUFFER_SIZE) + count = READ_BUFFER_SIZE; + read_len = modem_intf_read(dl_dev->read_buffer,count,0); + if (read_len>0){ + if (copy_to_user(buf,dl_dev->read_buffer,read_len)){ + dloader_unlock(&dl_dev->read_excl); + return -EFAULT; + } + } + } else { + + dloader_unlock(&dl_dev->read_excl); + return -EFAULT; + } + + dloader_unlock(&dl_dev->read_excl); + + return read_len; +} + +static int dloader_write(struct file *filp, const char __user *buf,size_t count,loff_t *pos) +{ + if ((dl_dev==NULL)||(dl_dev->open_count!=1)) + return -EIO; + + if (dloader_lock(&dl_dev->write_excl)) + return -EBUSY; + + if (count > WRITE_BUFFER_SIZE ) { + dloader_unlock(&dl_dev->write_excl); + return -EINVAL; + } + pr_debug("dloader_write: mode = %d\n",dl_dev->mode); + if (dl_dev->mode==MODEM_MODE_BOOT) { + if (copy_from_user(dl_dev->write_buffer,buf,count )){ + dloader_unlock(&dl_dev->write_excl); + return -EFAULT; + } + modem_intf_write(dl_dev->write_buffer,count,0); + } else { + + dloader_unlock(&dl_dev->write_excl); + return -EFAULT; + } + + dloader_unlock(&dl_dev->write_excl); + + return count; +} + +static int dloader_release(struct inode *inode,struct file *filp) +{ + printk(KERN_INFO "dloader_release time = 0x%x \n",jiffies); + kfree(dl_dev->read_buffer); + kfree(dl_dev->write_buffer); + dl_dev->read_buffer = NULL; + dl_dev->write_buffer = NULL; + wake_unlock(&dloader_wake_lock); + dl_dev->open_count--; + dl_dev->mode = MODEM_MODE_NORMAL; + modem_intf_send_bootcomp_message(); + + return 0; +} + +int dloader_abort() +{ + printk(KERN_INFO "dloader_abort!\n"); + dl_dev->mode = MODEM_MODE_NORMAL; + + return 0; +} + + +static struct file_operations dloader_fops = { + .owner = THIS_MODULE, + .read = dloader_read, + .write = dloader_write, + .open = dloader_open, + .release = dloader_release, +}; +static struct miscdevice dl_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = DLOADER_NAME, + .fops = &dloader_fops, +}; +static int __init init(void) +{ + struct dloader_dev *dev; + int err=0; + printk(KERN_INFO "dloader_init\n"); + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + dev->open_count = 0; + + atomic_set(&dev->read_excl, 0); + atomic_set(&dev ->write_excl, 0); + dl_dev = dev; + wake_lock_init(&dloader_wake_lock,WAKE_LOCK_SUSPEND,"dloader_wake_lock"); + err = misc_register(&dl_device); + + if(err){ + printk(KERN_INFO "cdev_add failed!!!\n"); + kfree(dev); + } + return err; +} +static void __exit cleanup(void) +{ + misc_deregister(&dl_device); + kfree(dl_dev); + dl_dev = NULL; +} +module_init(init); +module_exit(cleanup); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("SPRD boot driver"); diff --git a/drivers/char/modem_interface/boot_protocol.c b/drivers/char/modem_interface/boot_protocol.c new file mode 100644 index 00000000..ebb96a63 --- /dev/null +++ b/drivers/char/modem_interface/boot_protocol.c @@ -0,0 +1,417 @@ +/* + * kernel/driver/char/modem_interface/boot_protocol.c + * + * Generic modem boot protocol + * + * Author: Jiayong Yang(Jiayong.Yang@spreadtrum.com) + * Created: Jul 27, 2012 + * Copyright: Spreadtrum Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +/* #define DEBUG */ +/* #define VERBOSE_DEBUG */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/semaphore.h> +#include <linux/irqflags.h> +#include "modem_buffer.h" +#include "modem_interface_driver.h" + +#define SETUP_PACKET_SIZE 8 +#define ACK_PACKET_SIZE 6 +typedef struct dl_setup_packet{ + unsigned short data_packet_size; + unsigned short data_type; + unsigned short data_check_sum; + unsigned short setup_check_sum; +} SETUP_PACKET_T; +typedef enum MBUS_DL_status{ + MBUS_DL_IDLE, + MBUS_DL_WAIT_REQ, + MBUS_DL_WAIT_RTS, + MBUS_DL_SETUP, + MBUS_DL_SETUP_COMP, + MBUS_DL_DATA, + MBUS_DL_DATA_COMP, + MBUS_DL_ACK +}DL_STATUS_E; + + +extern void modem_intf_ctrl_gpio_handle_boot(int status); +extern int dloader_abort(); + +char *boot_status_string(DL_STATUS_E status) +{ + switch(status){ + case MBUS_DL_IDLE: return "DL_IDLE"; + case MBUS_DL_WAIT_REQ: return "DL_WAIT_REQ"; + case MBUS_DL_WAIT_RTS: return "DL_WAIT_RTS"; + case MBUS_DL_SETUP: return "DL_SETUP"; + case MBUS_DL_SETUP_COMP: return "DL_SETUP_COMP"; + case MBUS_DL_DATA: return "DL_DATA"; + case MBUS_DL_DATA_COMP: return "DL_DATA_COMP"; + case MBUS_DL_ACK: return "DL_ACK"; + default: break; + } + return "UNKNOWN status"; +} + + + +static void boot_idle(struct modem_message_node *msg,struct modem_intf_device *device) +{ + int send_bffer_index=0xFF; + switch(msg->type){ + case MODEM_SLAVE_RTS: + device->status = (int)MBUS_DL_WAIT_REQ; + break; + case MODEM_TRANSFER_REQ: + send_bffer_index = pingpang_buffer_send(&device->send_buffer); + if (send_bffer_index!= 0xFF) { + device->out_transfering = send_bffer_index; + device->status = (int)MBUS_DL_WAIT_RTS; + } + break; + case MODEM_SET_MODE: + modem_intf_set_mode(msg->parameter2, 1); + device->status = (int)MBUS_DL_IDLE; + device->out_transfering = 0; + dloader_abort(); + break; + default: + break; + } +} +static void boot_wait_request(struct modem_message_node *msg,struct modem_intf_device *device) +{ + int send_bffer_index=0xFF; + switch(msg->type){ + case MODEM_TRANSFER_REQ: + send_bffer_index = pingpang_buffer_send(&device->send_buffer); + if(send_bffer_index!= 0xFF){ + device->out_transfering = send_bffer_index; + device->op->write(device->send_buffer.buffer[send_bffer_index].addr,SETUP_PACKET_SIZE); + device->status = (int)MBUS_DL_SETUP; + } + break; + case MODEM_SET_MODE: + modem_intf_set_mode(msg->parameter2, 1); + device->status = (int)MBUS_DL_IDLE; + device->out_transfering = 0; + dloader_abort(); + break; + case MODEM_TRANSFER_END: + break; + default: + break; + } +} +static void boot_wait_RTS(struct modem_message_node *msg,struct modem_intf_device *device) +{ + int send_bffer_index=0xFF; + unsigned short *data; + switch(msg->type){ + case MODEM_SLAVE_RTS: + if(device->out_transfering < 2){ + send_bffer_index = device->out_transfering; + data = (unsigned short *)device->send_buffer.buffer[send_bffer_index].addr; + device->op->write((char *)data,SETUP_PACKET_SIZE); + device->status = (int)MBUS_DL_SETUP; + } + break; + case MODEM_TRANSFER_REQ: + device->out_transfer_pending = 1; + break; + case MODEM_SET_MODE: + modem_intf_set_mode(msg->parameter2, 1); + device->status = (int)MBUS_DL_IDLE; + device->out_transfering = 0; + dloader_abort(); + break; + default: + break; + } +} +static void boot_setup(struct modem_message_node *msg,struct modem_intf_device *device) +{ + int send_bffer_index=0xFF; + char *buffer; + int size; + switch(msg->type){ + case MODEM_TRANSFER_END: + device->status = (int)MBUS_DL_SETUP_COMP; + break; + case MODEM_TRANSFER_REQ: + device->out_transfer_pending = 1; + break; + case MODEM_SLAVE_RTS: + if(device->out_transfering < 2){ + send_bffer_index = device->out_transfering; + buffer = &device->send_buffer.buffer[send_bffer_index].addr[SETUP_PACKET_SIZE]; + size = device->send_buffer.buffer[send_bffer_index].write_point; + if (size <= SETUP_PACKET_SIZE) + size = 12; + device->op->write(buffer,size - SETUP_PACKET_SIZE); + pingpang_buffer_send_complete(&device->send_buffer,device->out_transfering); + device->out_transfering = 0xFF; + device->status = (int)MBUS_DL_DATA; + } + if(device->out_transfer_pending){ + send_bffer_index = pingpang_buffer_send(&device->send_buffer); + if(send_bffer_index!= 0xFF){ + device->out_transfering = send_bffer_index; + size = device->send_buffer.buffer[send_bffer_index].write_point; + device->op->write(device->send_buffer.buffer[send_bffer_index].addr,size); + } + device->out_transfer_pending = 0; + } + break; + case MODEM_SET_MODE: + modem_intf_set_mode(msg->parameter2, 1); + device->status = (int)MBUS_DL_IDLE; + device->out_transfering = 0; + dloader_abort(); + break; + default: + break; + } +} + +static void boot_setup_comp(struct modem_message_node *msg,struct modem_intf_device *device) +{ + int send_bffer_index=0xFF; + char *buffer; + int size; + + switch(msg->type){ + case MODEM_SLAVE_RTS: + if(device->out_transfering < 2){ + send_bffer_index = device->out_transfering; + buffer = &device->send_buffer.buffer[send_bffer_index].addr[SETUP_PACKET_SIZE]; + size = device->send_buffer.buffer[send_bffer_index].write_point; + if (size <= SETUP_PACKET_SIZE) + size = 12; + device->op->write(buffer,size - SETUP_PACKET_SIZE); + device->status = (int)MBUS_DL_DATA; + if(device->out_transfer_pending){ + pingpang_buffer_send_complete(&device->send_buffer,device->out_transfering); + device->out_transfering = 0xFF; + send_bffer_index = pingpang_buffer_send(&device->send_buffer); + if(send_bffer_index!= 0xFF){ + device->out_transfering = send_bffer_index; + size = device->send_buffer.buffer[send_bffer_index].write_point; + device->op->write(device->send_buffer.buffer[send_bffer_index].addr,size); + } + device->out_transfer_pending = 0; + } + } + break; + case MODEM_TRANSFER_REQ: + device->out_transfer_pending = 1; + break; + case MODEM_SET_MODE: + modem_intf_set_mode(msg->parameter2, 1); + device->status = (int)MBUS_DL_IDLE; + device->out_transfering = 0; + dloader_abort(); + break; + default: + break; + + } +} +static void boot_data(struct modem_message_node *msg,struct modem_intf_device *device) +{ + unsigned short *data; + int send_bffer_index=0xFF; + char *buffer; + int size; + switch(msg->type){ + case MODEM_TRANSFER_END: + if(device->out_transfering < 2){ + pingpang_buffer_send_complete(&device->send_buffer,device->out_transfering); + device->out_transfering = 0xFF; + device->status = (int)MBUS_DL_DATA_COMP; + } + break; + case MODEM_TRANSFER_REQ: + if(device->out_transfering < 2){ + pingpang_buffer_send_complete(&device->send_buffer,device->out_transfering); + device->out_transfering = 0xFF; + } + send_bffer_index = pingpang_buffer_send(&device->send_buffer); + if(send_bffer_index!= 0xFF){ + device->out_transfering = send_bffer_index; + size = device->send_buffer.buffer[send_bffer_index].write_point; + device->op->write(device->send_buffer.buffer[send_bffer_index].addr,size); + } + break; + case MODEM_SLAVE_RTS: + pingpang_buffer_send_complete(&device->send_buffer,device->out_transfering); + device->op->read(device->recv_buffer.buffer[1].addr,8); + device->out_transfering = 0xFF; + data = (unsigned short *)device->recv_buffer.buffer[1].addr; + device->status = (int)MBUS_DL_ACK; + if(device->out_transfer_pending){ + send_bffer_index = pingpang_buffer_send(&device->send_buffer); + if(send_bffer_index!= 0xFF){ + device->out_transfering = send_bffer_index; + size = device->send_buffer.buffer[send_bffer_index].write_point; + device->op->write(device->send_buffer.buffer[send_bffer_index].addr,size); + } + device->out_transfer_pending = 0; + } + break; + case MODEM_SET_MODE: + modem_intf_set_mode(msg->parameter2, 1); + device->status = (int)MBUS_DL_IDLE; + device->out_transfering = 0; + dloader_abort(); + break; + default: + break; + } +} +static void boot_data_comp(struct modem_message_node *msg,struct modem_intf_device *device) +{ + unsigned short *data; + switch(msg->type){ + case MODEM_SLAVE_RTS: + device->op->read(device->recv_buffer.buffer[1].addr,8); + data = (unsigned short *)device->recv_buffer.buffer[1].addr; + device->status = (int)MBUS_DL_ACK; + break; + case MODEM_TRANSFER_REQ: + device->out_transfer_pending = 1; + break; + case MODEM_SET_MODE: + modem_intf_set_mode(msg->parameter2, 1); + device->status = (int)MBUS_DL_IDLE; + device->out_transfering = 0; + dloader_abort(); + break; + default: + break; + } +} +static void boot_ack(struct modem_message_node *msg,struct modem_intf_device *device) +{ + int send_bffer_index=0xFF; + unsigned short *data; + + switch(msg->type){ + case MODEM_TRANSFER_END: + save_to_receive_buffer(&device->recv_buffer,device->recv_buffer.buffer[1].addr,ACK_PACKET_SIZE); + if(device->out_transfer_pending){ + device->out_transfer_pending = 0; + device->out_transfering = send_bffer_index = pingpang_buffer_send(&device->send_buffer); + if(device->out_transfering!=0xff){ + device->status = (int)MBUS_DL_WAIT_RTS; + break; + } + } + device->status = (int)MBUS_DL_IDLE; + break; + case MODEM_TRANSFER_REQ: + device->out_transfer_pending = 1; + break; + case MODEM_SLAVE_RTS: + data = (unsigned short *)device->recv_buffer.buffer[1].addr; + save_to_receive_buffer(&device->recv_buffer,data,ACK_PACKET_SIZE); + if(device->out_transfer_pending){ + device->out_transfer_pending = 0; + device->out_transfering = send_bffer_index = pingpang_buffer_send(&device->send_buffer); + + data = (unsigned short *)device->send_buffer.buffer[send_bffer_index].addr; + device->op->write((char *)data,SETUP_PACKET_SIZE); + + device->status = (int)MBUS_DL_SETUP; + } else { + device->status = (int)MBUS_DL_WAIT_REQ; + } + break; + case MODEM_SET_MODE: + modem_intf_set_mode(msg->parameter2, 1); + device->status = (int)MBUS_DL_IDLE; + device->out_transfering = 0; + dloader_abort(); + break; + default: + break; + } +} +extern unsigned long sprd_get_system_tick(void); +extern void dloader_record_timestamp(unsigned long time); + +void boot_protocol_process_dl_messages(struct modem_intf_device *device, + struct modem_message_node *msg) +{ + DL_STATUS_E status; + + status =(DL_STATUS_E) device->status; + + switch(status){ + case MBUS_DL_IDLE: + boot_idle(msg,device); + break; + case MBUS_DL_SETUP: + boot_setup(msg,device); + break; + case MBUS_DL_WAIT_REQ: + boot_wait_request(msg,device); + break; + case MBUS_DL_WAIT_RTS: + boot_wait_RTS(msg,device); + break; + case MBUS_DL_SETUP_COMP: + boot_setup_comp(msg,device); + break; + case MBUS_DL_DATA: + boot_data(msg,device); + break; + case MBUS_DL_DATA_COMP: + boot_data_comp(msg,device); + break; + case MBUS_DL_ACK: + boot_ack(msg,device); + break; + } +} + +void boot_protocol(struct modem_message_node *msg) +{ + struct modem_intf_device *device; + + if((msg == NULL)||(msg->parameter1==0)) + return; + + device = (struct modem_intf_device *)msg->parameter1; + + printk(">>boot entry(%d,%d) status: %s message: %s \n", + device->out_transfer_pending,device->out_transfering, + boot_status_string((DL_STATUS_E )device->status), + modem_intf_msg_string(msg->type)); + + //filter messages + switch(msg->type){ + case MODEM_CTRL_GPIO_CHG: + modem_intf_ctrl_gpio_handle_boot(msg->parameter2); + break; + case MODEM_USER_REQ: + //now, just ignore user req in boot mode + break; + case MODEM_BOOT_CMP: + modem_intf_set_mode(MODEM_MODE_BOOTCOMP, 0); + break; + default: + //all other messages + boot_protocol_process_dl_messages(device, msg); + break; + } + +} diff --git a/drivers/char/modem_interface/modem_buffer.c b/drivers/char/modem_interface/modem_buffer.c new file mode 100644 index 00000000..e42a5a74 --- /dev/null +++ b/drivers/char/modem_interface/modem_buffer.c @@ -0,0 +1,256 @@ +/* + * kernel/driver/char/modem_interface/modem_buffer.c + * + * Generic modem interface pingpang buffer API. + * + * Author: Jiayong Yang(Jiayong.Yang@spreadtrum.com) + * Created: Jul 27, 2012 + * Copyright: Spreadtrum Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +/* #define DEBUG */ +/* #define VERBOSE_DEBUG */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <mach/modem_interface.h> +#include <linux/kernel.h> +#include <linux/utsname.h> +#include <linux/semaphore.h> +#include <linux/irqflags.h> +#include "modem_buffer.h" +#define dloader_record_timestamp(time) + +spinlock_t buffer_lock; +static int buffer_data_size(struct single_buffer_t *buffer) +{ + int data_size = 0; + if (buffer->write_point == buffer->read_point) + return 0; + data_size = buffer->write_point - buffer->read_point; + if (buffer->write_point < buffer->read_point) + data_size = buffer->size + data_size; + return data_size; +} + +int save_to_receive_buffer(struct modem_buffer *buffer,char *data,int size) +{ + int free_space = 0; + int data_size = 0; + int i; + int write_point= buffer->buffer[0].write_point; + + if (buffer->type==BUF_RECV) { + do{ + data_size = buffer_data_size(&buffer->buffer[0]); + free_space = buffer->buffer[0].size-1-data_size; + if (free_space >= size) + break; + down(&buffer->buf_read_sem); + }while(1); + for(i=0;i<size;i++){ + buffer->buffer[0].addr[write_point++]=data[i]; + if(write_point >= buffer->buffer[0].size) + write_point = 0; + } + buffer->buffer[0].write_point = write_point; + up(&buffer->buf_read_sem); + return size; + } + return 0; +} + +int pingpang_buffer_read(struct modem_buffer *buffer,char *data,int size) +{ + int read_len = 0; + int data_size = 0; + int i; + int read_point= buffer->buffer[0].read_point; + + if (buffer->type==BUF_RECV) { + do{ + if (buffer->buffer[0].write_point != buffer->buffer[0].read_point) + break; + down(&buffer->buf_read_sem); + }while(1); + data_size = buffer_data_size(&buffer->buffer[0]); + if(data_size==0) + return 0; + + if(data_size < size){ + read_len = data_size; + } else { + read_len = size; + } + for (i=0;i<read_len;i++) { + data[i]= buffer->buffer[0].addr[read_point++]; + if(read_point >= buffer->buffer[0].size) + read_point = 0; + } + dloader_record_timestamp(0x30000000|buffer->buffer[0].read_point); + dloader_record_timestamp(0x40000000|read_point); + buffer->buffer[0].read_point = read_point; + up(&buffer->buf_read_sem); + return read_len; + } + return 0; +} + +int pingpang_buffer_send(struct modem_buffer *buffer) +{ + enum BUF_status_t status; + int i,index; + unsigned long flags; + + if(buffer->type == BUF_SEND){ + if(buffer->trans_index!=0xFF) + index = buffer->trans_index; + else + index = buffer->save_index; + local_irq_save(flags); + spin_lock_irqsave(&buffer_lock, flags); + for(i=0;i<2;i++){ + status = buffer->buffer[index].status; + if ((status != BUF_STATUS_IDLE) && (status != BUF_STATUS_WRITTEN)) { + buffer->buffer[index].status = BUF_STATUS_SENT; + local_irq_restore(flags); + spin_unlock_irqrestore(&buffer_lock, flags); + buffer->trans_index = index; + dloader_record_timestamp(0x50000000|(index<<24)|buffer->buffer[index].write_point); + if(buffer->save_index == index) + buffer->save_index = 1- index; + return index; + } + index = 1 - index; + } + local_irq_restore(flags); + spin_unlock_irqrestore(&buffer_lock, flags); + } + return 0xFF; +} + +void pingpang_buffer_send_complete(struct modem_buffer *buffer,int index) +{ + if(buffer->type == BUF_SEND){ + if(buffer->trans_index == index){ + buffer->trans_count += buffer->buffer[index].write_point; + buffer->buffer[index].write_point = 0; + buffer->buffer[index].read_point = 0; + buffer->trans_index = 0xFF; + buffer->buffer[index].status = BUF_STATUS_IDLE; + up(&buffer->buf_write_sem); + } + } +} + +int pingpang_buffer_write(struct modem_buffer *buffer,char *data,int size) +{ + int index = 0; + int data_size; + int free_space; + int write_point; + int save_flag = 0; + int i; + unsigned long flags; + enum BUF_status_t status; + + if(buffer->type == BUF_SEND){ + if (size >= buffer->buffer[0].size) + return 0; + + do{ + index = buffer->save_index; + for(i=0;i<2;i++){ + local_irq_save(flags); + status = buffer->buffer[index].status; + if ((status != BUF_STATUS_SENT) && + (status != BUF_STATUS_FULL) && + (status != BUF_STATUS_WRITTEN)) { + buffer->buffer[index].status = BUF_STATUS_WRITTEN; + local_irq_restore(flags); + data_size = buffer_data_size(&buffer->buffer[index]); + free_space = buffer->buffer[index].size -1 - data_size; + if(free_space < size){ + buffer->buffer[index].status = BUF_STATUS_FULL; + if(buffer->trans_index == 0xFF) + buffer->trans_index = index; + index = 1 - index; + buffer->save_index = index; + } else { + write_point = buffer->buffer[index].write_point; + for(i=0;i<size;i++){ + buffer->buffer[index].addr[write_point++]=data[i]; + if(write_point >= buffer->buffer[index].size) + write_point = 0; + } + dloader_record_timestamp(0x10000000|(index<<24)|buffer->buffer[index].write_point); + dloader_record_timestamp(0x20000000|(index<<24)|write_point); + buffer->buffer[index].write_point = write_point; + buffer->buffer[index].status = BUF_STATUS_NOEMPTY; + buffer->buffer[index].status = BUF_STATUS_FULL; + if(buffer->trans_index == 0xFF) + buffer->trans_index = index; + save_flag = 1; + } + } else { + local_irq_restore(flags); + index = 1 - index; + } + } + if(save_flag==0) + down(&buffer->buf_write_sem); + }while(save_flag == 0); + } + return size; +} +int pingpang_buffer_init(struct modem_buffer *buffer) +{ + char *address; + int divide = 2; + + address = kzalloc(RECV_BUFFER_SIZE + 128, GFP_KERNEL); + if (address == NULL) + return -ENOMEM; + + if (buffer->type == BUF_RECV) + divide = 4; + + buffer->buffer[0].addr = address+32; + buffer->buffer[0].size = RECV_BUFFER_SIZE - RECV_BUFFER_SIZE/divide; + buffer->buffer[0].write_point = 0; + buffer->buffer[0].read_point = 0; + buffer->buffer[0].status = BUF_STATUS_IDLE; + buffer->buffer[1].addr = address + buffer->buffer[0].size+64; + buffer->buffer[1].size = RECV_BUFFER_SIZE - buffer->buffer[0].size; + buffer->buffer[1].read_point = 0; + buffer->buffer[1].write_point = 0; + buffer->buffer[1].status = BUF_STATUS_IDLE; + buffer->lost_count = 0; + buffer->trans_count = 0; + buffer->save_index = 0; + buffer->trans_index = 0xff; + sema_init(&buffer->buf_read_sem,0); + sema_init(&buffer->buf_write_sem,0); + spin_lock_init(&buffer_lock); + return 0; +} +void pingpang_buffer_free(struct modem_buffer *buffer) +{ + if (buffer->buffer[0].addr){ + kfree(buffer->buffer[0].addr); + buffer->buffer[0].addr = NULL; + buffer->buffer[0].size = 0; + buffer->buffer[0].write_point = 0; + buffer->buffer[0].read_point = 0; + buffer->buffer[0].status = 0; + buffer->buffer[1].addr = NULL; + buffer->buffer[1].size = 0; + buffer->buffer[1].read_point = 0; + buffer->buffer[1].write_point = 0; + buffer->buffer[1].status = 0; + } +} diff --git a/drivers/char/modem_interface/modem_buffer.h b/drivers/char/modem_interface/modem_buffer.h new file mode 100644 index 00000000..ac31efef --- /dev/null +++ b/drivers/char/modem_interface/modem_buffer.h @@ -0,0 +1,58 @@ +/* + * kernel/driver/char/modem_interface/modem_buffer.h + * + * Generic modem interface buffer defination. + * + * Author: Jiayong Yang(Jiayong.Yang@spreadtrum.com) + * Created: Jul 27, 2012 + * Copyright: Spreadtrum Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef _MODEM_BUFFER_H +#define _MODEM_BUFFER_H + +#define RECV_BUFFER_SIZE (65*1024) +enum BUF_type_t{ + BUF_SEND, + BUF_RECV, +}; + +enum BUF_status_t{ + BUF_STATUS_IDLE, + BUF_STATUS_NOEMPTY, + BUF_STATUS_WRITTEN, + BUF_STATUS_FULL, + BUF_STATUS_SENT, +}; + +struct single_buffer_t{ + char *addr; + int size; + int write_point; + int read_point; + enum BUF_status_t status; +}; + +struct modem_buffer{ + struct mutex mutex; + struct semaphore buf_read_sem; + struct semaphore buf_write_sem; + enum BUF_type_t type; + int save_index; + int trans_index; + int trans_count; + int lost_count; + struct single_buffer_t buffer[2]; +}; + +extern int pingpang_buffer_send(struct modem_buffer *buffer); +extern int pingpang_buffer_init(struct modem_buffer *buffer); +extern void pingpang_buffer_free(struct modem_buffer *buffer); +extern void pingpang_buffer_send_complete(struct modem_buffer *buffer,int index); +extern int pingpang_buffer_read(struct modem_buffer *buffer,char *data,int size); +extern int pingpang_buffer_write(struct modem_buffer *buffer,char *data,int size); +extern int save_to_receive_buffer(struct modem_buffer *buffer,char *data,int size); +#endif diff --git a/drivers/char/modem_interface/modem_gpio_drv.c b/drivers/char/modem_interface/modem_gpio_drv.c new file mode 100644 index 00000000..7851fb00 --- /dev/null +++ b/drivers/char/modem_interface/modem_gpio_drv.c @@ -0,0 +1,358 @@ +/* + * kernel/driver/char/modem_interface/modem_gpio_drv.c + * + * Generic modem interface gpio handling. + * + * Author: Jiayong Yang(Jiayong.Yang@spreadtrum.com) + * Created: Jul 27, 2012 + * Copyright: Spreadtrum Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/mutex.h> +#include <linux/dma-mapping.h> +#include <linux/proc_fs.h> +#include <linux/gpio.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <mach/modem_interface.h> +#include <mach/globalregs.h> +#include "modem_buffer.h" +#include "modem_interface_driver.h" + +#define GPIO_INVALID 0xFFFFFFFF + +extern void free_irq(unsigned int irq, void *dev_id); +extern void modem_intf_send_GPIO_message(int gpio_no,int status,int index); +extern void modem_intf_send_ctrl_gpio_message(enum MSG_CTRL_GPIO_TYPE gpio_type, int gpio_value); +extern void on_modem_poweron(void); +static unsigned int cp_to_ap_rdy_irq; +static unsigned int cp_alive_gpio_irq; +static unsigned int cp_crash_gpio_irq; +static unsigned int cp_watchdog_gpio_irq; +static unsigned int modem_power_gpio; +static unsigned int modem_reset_gpio; +static int cp_to_ap_rdy ; +static int cp_alive_gpio; +static int cp_crash_gpio; +static int cp_watchdog_gpio; + + +int get_alive_status(void) +{ + int status = gpio_get_value(cp_alive_gpio); + if(status != 0) + return 1; + return 0; +} +int get_assert_status(void) +{ + int status = gpio_get_value(cp_crash_gpio); + if(status != 0) + return 1; + return 0; +} +static irqreturn_t cp_to_ap_rdy_handle(int irq, void *handle) +{ + int status ; + + status = gpio_get_value(cp_to_ap_rdy); + pr_debug("modem_boot_irq: %d \n",status); + if(status == 0) + modem_intf_send_GPIO_message(cp_to_ap_rdy,status,0); + else + modem_intf_send_GPIO_message(cp_to_ap_rdy,1,0); + return IRQ_HANDLED; +} + +int modem_intf_get_watchdog_gpio_value() +{ + + return gpio_get_value(cp_watchdog_gpio); +} +static irqreturn_t cp_watchdog_gpio_handle(int irq, void * handle) +{ + int wdg_reset_status; + + wdg_reset_status = gpio_get_value(cp_watchdog_gpio); + printk("cp_watchdog_gpio: %d \n",wdg_reset_status); + + if(wdg_reset_status == 0) { + irq_set_irq_type(cp_watchdog_gpio_irq, IRQ_TYPE_LEVEL_HIGH); + } else { + irq_set_irq_type(cp_watchdog_gpio_irq, IRQ_TYPE_LEVEL_LOW); + } + + modem_intf_send_ctrl_gpio_message(GPIO_WATCHDOG, wdg_reset_status); + return IRQ_HANDLED; +} + +static irqreturn_t cp_alive_gpio_handle(int irq, void *handle) +{ + int alive_status; + + alive_status = gpio_get_value(cp_alive_gpio); + printk("cp_alive_gpio: %d \n",alive_status); + + if(alive_status == 0) { + irq_set_irq_type(cp_alive_gpio_irq, IRQ_TYPE_LEVEL_HIGH); + } else { + irq_set_irq_type(cp_alive_gpio_irq, IRQ_TYPE_LEVEL_LOW); + } + + modem_intf_send_ctrl_gpio_message(GPIO_ALIVE, alive_status); + return IRQ_HANDLED; +} + + +static irqreturn_t cp_crash_gpio_handle(int irq, void *handle) +{ + int assert_status; + + assert_status = gpio_get_value(cp_crash_gpio); + printk("cp_crash_gpio: %d \n",assert_status); + + if(assert_status == 0) { + irq_set_irq_type(cp_crash_gpio_irq, IRQ_TYPE_LEVEL_HIGH); + } else { + irq_set_irq_type(cp_crash_gpio_irq, IRQ_TYPE_LEVEL_LOW); + } + + modem_intf_send_ctrl_gpio_message(GPIO_RESET, assert_status); + return IRQ_HANDLED; +} + +int modem_share_gpio_init(void *para) +{ + struct modem_intf_platform_data *modem_config = (struct modem_intf_platform_data *)para; + int error = 0; + int status = 0; + + pr_debug("modem_share_gpio_init start!!! \n"); + cp_to_ap_rdy = modem_config->modem_boot_gpio; + + error = gpio_request(cp_to_ap_rdy, "modem_boot_gpio"); + if (error) { + pr_err("Cannot request GPIO %d\n", cp_to_ap_rdy); + gpio_free(cp_to_ap_rdy); + return error; + } + + gpio_direction_input(cp_to_ap_rdy); + gpio_export(cp_to_ap_rdy,0); + cp_to_ap_rdy_irq = gpio_to_irq(cp_to_ap_rdy); + if (cp_to_ap_rdy_irq < 0) + return -1; + error = request_threaded_irq(cp_to_ap_rdy_irq, cp_to_ap_rdy_handle, + NULL, IRQF_DISABLED|IRQF_TRIGGER_RISING |IRQF_TRIGGER_FALLING, "modem_boot", para); + if (error) { + printk("lee :cannot alloc cp_to_ap_rdy_irq, err %d\r\n", error); + gpio_free(cp_to_ap_rdy); + return error; + } + + status = gpio_get_value(cp_to_ap_rdy); + if(status == 0) + modem_intf_send_GPIO_message(cp_to_ap_rdy,status,0); + else + modem_intf_send_GPIO_message(cp_to_ap_rdy,1,0); + + return 0; +} + +int modem_share_gpio_uninit(void *para) +{ + struct modem_intf_platform_data *modem_config = (struct nodem_intf_platform_data *)para; + + pr_debug("modem_gpio_init start!!! \n"); + cp_to_ap_rdy = modem_config->modem_boot_gpio; + + cp_to_ap_rdy_irq = gpio_to_irq(cp_to_ap_rdy); + if (cp_to_ap_rdy_irq < 0) + return -1; + free_irq( cp_to_ap_rdy_irq, para); + gpio_free(cp_to_ap_rdy); + + return 0; +} + +int modem_gpio_irq_init(void *para) +{ + int error = 0; + + cp_watchdog_gpio_irq = gpio_to_irq(cp_watchdog_gpio); + if(cp_watchdog_gpio_irq < 0) + return -1; + error = request_threaded_irq(cp_watchdog_gpio_irq, cp_watchdog_gpio_handle, + NULL, IRQF_DISABLED|IRQF_NO_SUSPEND |IRQF_TRIGGER_LOW, "modem_watchdog", para); + if (error) { + printk("modem_intf :cannot alloc cp_watchdog irq, err %d\r\n", error); + gpio_free(cp_watchdog_gpio); + return error; + } + + enable_irq_wake(cp_watchdog_gpio_irq); + + cp_alive_gpio_irq = gpio_to_irq(cp_alive_gpio); + if (cp_alive_gpio_irq < 0) + return -1; + error = request_threaded_irq(cp_alive_gpio_irq, cp_alive_gpio_handle, + NULL, IRQF_DISABLED|IRQF_NO_SUSPEND |IRQF_TRIGGER_HIGH, "modem_alive", para); + if (error) { + printk("modem_intf :cannot alloc cp_alive irq, err %d\r\n", error); + gpio_free(cp_alive_gpio); + return error; + } + + enable_irq_wake(cp_alive_gpio_irq); + + cp_crash_gpio_irq = gpio_to_irq(cp_crash_gpio); + if (cp_crash_gpio_irq < 0) + return -1; + error = request_threaded_irq(cp_crash_gpio_irq, cp_crash_gpio_handle, + NULL, IRQF_DISABLED|IRQF_NO_SUSPEND |IRQF_TRIGGER_HIGH, "modem_crash", para); + if (error) { + printk("modem_intf :cannot alloc cp_crash irq, err %d\r\n", error); + gpio_free(cp_crash_gpio); + return error; + } + + enable_irq_wake(cp_crash_gpio_irq); +} + + +int modem_gpio_init(void *para) +{ + struct modem_intf_platform_data *modem_config = (struct modem_intf_platform_data *)para; + int error = 0; + + pr_debug("modem_gpio_init start!!! \n"); + cp_to_ap_rdy = modem_config->modem_boot_gpio; + cp_watchdog_gpio = modem_config->modem_watchdog_gpio; + cp_alive_gpio = modem_config->modem_alive_gpio; + cp_crash_gpio = modem_config->modem_crash_gpio; + modem_power_gpio = modem_config->modem_power_gpio; + modem_reset_gpio = modem_config->modem_reset_gpio; + + if(GPIO_INVALID != modem_power_gpio) { + gpio_request(modem_power_gpio, "modem_power_gpio"); + gpio_export(modem_power_gpio,0); + } + + if(GPIO_INVALID != modem_reset_gpio) { + gpio_request(modem_reset_gpio, "modem_reset_gpio"); + gpio_export(modem_reset_gpio,0); + } + + //watchdog gpio + error = gpio_request(cp_watchdog_gpio, "modem_watchdog_gpio"); + if (error) { + pr_err("Cannot request GPIO %d\n", cp_watchdog_gpio); + gpio_free(cp_watchdog_gpio); + return error; + } + gpio_export(cp_watchdog_gpio,0); + gpio_direction_input(cp_watchdog_gpio); + + +////////////////////////////////////////////////// + //alive gpio + error = gpio_request(cp_alive_gpio, "modem_alive_gpio"); + if (error) { + pr_err("Cannot request GPIO %d\n", cp_alive_gpio); + gpio_free(cp_alive_gpio); + return error; + } + gpio_export(cp_alive_gpio,0); + gpio_direction_input(cp_alive_gpio); + + //crash gpio + error = gpio_request(cp_crash_gpio, "modem_crash_gpio"); + if (error) { + pr_err("Cannot request GPIO %d\n", cp_crash_gpio); + gpio_free(cp_crash_gpio); + return error; + } + gpio_export(cp_crash_gpio,0); + gpio_direction_input(cp_crash_gpio); + +///////////////////////////////////////////////// + + return 0; +} + +int modem_gpio_uninit(void *para) +{ + modem_share_gpio_uninit(para); + return 0; +} + +void modem_poweron_async_step1(void) +{ + if(GPIO_INVALID != modem_power_gpio) { + printk(KERN_ERR "modem_poweron_async_step1!!! \n"); + gpio_direction_output(modem_power_gpio, 1); + } else { + printk(KERN_ERR "modem_poweron_async_step1 modem_power_gpio == GPIO_INVALID!!! \n"); + } +} + +void modem_poweron_async_step2(void) +{ + if(GPIO_INVALID != modem_power_gpio) { + gpio_set_value(modem_power_gpio, 0); + printk(KERN_ERR "modem_poweron_async_step2!!! \n"); + } else { + printk(KERN_ERR "modem_poweron_async_step2 modem_power_gpio == GPIO_INVALID!!! \n"); + } +} + +void modem_poweron(void) +{ + printk("modem power on!!! \n"); + if(GPIO_INVALID != modem_power_gpio) { + gpio_direction_output(modem_power_gpio, 1); //2012.1.10 + //gpio_set_value(modem_power_gpio, 1); + msleep(2000); + gpio_set_value(modem_power_gpio, 0); + } else { + //sprd_greg_clear_bits(REG_TYPE_GLOBAL, 0x00000040, 0x4C); + } +} +void modem_poweroff(void) +{ + printk("modem power off!!! \n"); + if (GPIO_INVALID != modem_power_gpio) { + gpio_direction_output(modem_power_gpio, 1); //2012.1.10 + //gpio_set_value(modem_power_gpio, 1); + msleep(3500);//should be matched with Adie config of cp + gpio_set_value(modem_power_gpio, 0); + } else { + //sprd_greg_set_bits(REG_TYPE_GLOBAL, 0x00000040, 0x4C); + } +} + +void modem_soft_reset(void) +{ + printk(KERN_INFO "modem soft reset enter \n"); + if (GPIO_INVALID != modem_reset_gpio) { + gpio_direction_output(modem_reset_gpio, 1); //2012.1.10 + //gpio_set_value(modem_power_gpio, 1); + msleep(50); + gpio_set_value(modem_reset_gpio, 0); + } +} + +void modem_hard_reset(void) +{ + printk(KERN_INFO "modem hard reset enter \n"); + if (GPIO_INVALID != modem_power_gpio) { + gpio_direction_output(modem_power_gpio, 1); //2012.1.10 + //gpio_set_value(modem_power_gpio, 1); + msleep(7100); + gpio_set_value(modem_power_gpio, 0); + } +} diff --git a/drivers/char/modem_interface/modem_interface_driver.c b/drivers/char/modem_interface/modem_interface_driver.c new file mode 100644 index 00000000..862c32ea --- /dev/null +++ b/drivers/char/modem_interface/modem_interface_driver.c @@ -0,0 +1,1088 @@ +/* driver/char/modem_interface/modem_interface_drv.c + * + * modem interface API for dloader driver and API for application layer + * + * Copyright (C) 2012 Spreadtrum + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/semaphore.h> +#include <mach/modem_interface.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/list.h> +#include <linux/wait.h> +#include <linux/wakelock.h> +#include <linux/sched.h> +#include<linux/spinlock.h> +#include "modem_buffer.h" +#include "modem_interface_driver.h" +#include <mach/globalregs.h> +#include <linux/kthread.h> +#include <linux/delay.h> +#include <linux/timer.h> + + +#define MAX_MSG_NODE_NUM 32 +#define BUSY_BIT 0x8000 +#define MAX_WAIT_WATCHDOG_TIME 5 + +extern int get_alive_status(void); +extern int get_assert_status(void); +extern int modem_gpio_uninit(void *para); +extern int modem_gpio_init(void *para); +extern int modem_share_gpio_uninit(void *para); +extern int modem_share_gpio_init(void *para); +extern void mux_ipc_enable(u8 is_enable); +extern void modem_gpio_irq_init(void* para); +extern void modem_poweron_async_step1(void); +extern void modem_poweron_async_step2(void); + +extern struct modem_device_operation *modem_sdio_drv_init(void); +extern int modem_gpio_init(void *para); +extern void modem_poweron(void); +extern void modem_poweroff(void); +extern void Set_modem_status(unsigned char modem_status); +extern void modem_soft_reset(void); +extern void modem_hard_reset(void); +extern void sdio_ipc_enable(u8 is_enable); +extern void shutdown_protocol(struct modem_message_node *msg); +extern void bootcomp_protocol(struct modem_message_node *msg); +extern void reset_protocol(struct modem_message_node *msg); +extern int modem_intf_get_watchdog_gpio_value(); + + + +int modem_intf_send_boot_message(void); +void modem_intf_reboot_routine(void); +void modem_intf_alive_routine(void); +void modem_intf_assert_routine(void); +void modem_intf_wdg_reset_routine(void); + + +static struct modem_intf_device *modem_intf_device = NULL; + +static struct modem_message_node *msg_nodes=NULL; +static atomic_t nodes_count; +static int lost_msg_count; +static LIST_HEAD(msg_list); +static struct semaphore msg_list_sem; +static struct semaphore modem_event_sem; +static int modem_gpio_status[CTRL_GPIO_MAX]; +static int modem_event = MODEM_INTF_EVENT_SHUTDOWN; +static struct wake_lock s_modem_intf_proc_wake_lock; +static struct wake_lock s_modem_intf_msg_wake_lock; +static struct wake_lock s_modem_intf_event_wake_lock; +static struct timer_list s_modem_watch_timer; + + + +//wait_queue_head_t mdoem_mode_normal; +spinlock_t int_lock; +struct modem_device_operation *devices_op[8] = {NULL}; + +/************ modemsts notifier *****************/ +static LIST_HEAD(modemsts_chg_handlers); +static DEFINE_MUTEX(modemsts_chg_lock); + +/* register a callback function when modem status changed +* @handler: callback function +*/ +int modemsts_notifier_register(struct modemsts_chg *handler) +{ + + struct list_head *pos; + struct modemsts_chg *e; + + mutex_lock(&modemsts_chg_lock); + list_for_each(pos, &modemsts_chg_handlers) { + e = list_entry(pos, struct modemsts_chg, link); + if(e == handler){ + printk("***** %s, %pf already exsited ****\n", + __func__, e->modemsts_notifier); + return -1; + } + } + list_for_each(pos, &modemsts_chg_handlers) { + struct modemsts_chg *e; + e = list_entry(pos, struct modemsts_chg, link); + if (e->level > handler->level) + break; + } + list_add_tail(&handler->link, pos); + mutex_unlock(&modemsts_chg_lock); + + return 0; +} +EXPORT_SYMBOL(modemsts_notifier_register); + +/* unregister a callback function for detecting modem status change +* @handler: callback function +*/ +int modemsts_notifier_unregister(struct modemsts_chg *handler) +{ + mutex_lock(&modemsts_chg_lock); + list_del(&handler->link); + mutex_unlock(&modemsts_chg_lock); + + return 0; +} +EXPORT_SYMBOL(modemsts_notifier_unregister); + +void modemsts_change_notification(unsigned int state) +{ + struct modemsts_chg *pos; + + mutex_lock(&modemsts_chg_lock); + if(state == MODEM_STATUS_REBOOT) { + list_for_each_entry(pos, &modemsts_chg_handlers, link) { + if (pos->modemsts_notifier != NULL) { + pos->modemsts_notifier(pos, state); + } + } + } else { + list_for_each_entry_reverse(pos, &modemsts_chg_handlers, link) { + if (pos->modemsts_notifier != NULL) { + pos->modemsts_notifier(pos, state); + } + } + } + mutex_unlock(&modemsts_chg_lock); +} +/************ modem notifier *****************/ + + +char *modem_intf_msg_string(enum MODEM_MSG_type msg) +{ + switch(msg) { + case MODEM_TRANSFER_REQ: + return "MODEM_TRANSFER_REQ"; + case MODEM_TRANSFER_END: + return "MODEM_TRANSFER_END"; + case MODEM_SLAVE_RTS: + return "MODEM_SLAVE_RTS"; + case MODEM_SLAVE_RDY: + return "MODEM_SLAVE_RDY"; + case MODEM_SET_MODE: + return "MODEM_SET_MODE"; + case MODEM_CTRL_GPIO_CHG: + return "MODEM_CTRL_GPIO_CHG"; + case MODEM_USER_REQ: + return "MODEM_USER_REQ"; + case MODEM_BOOT_CMP: + return "MODEM_BOOT_CMP"; + case MODEM_OPEN_DEVICE: + return "MODEM_OPEN_DEVICE"; + default: + break; + } + return "UNKNOWN MSG"; +} +static void modem_intf_register_device_operation(struct modem_device_operation *op) +{ + int i; + for(i=0; i<8; i++) { + if(devices_op[i] == NULL) + devices_op[i] = op; + } +} +static struct modem_device_operation *modem_intf_get_device_operation(enum MODEM_device_type dev) { + int i; + for(i=0; i<8; i++) { + if((devices_op[i] != NULL)&&(devices_op[i]->dev == dev)) + return devices_op[i]; + } + return NULL; +} +static int init_msg_nodes(void) +{ + int i; + + if(msg_nodes) + return 0; + + msg_nodes = kzalloc(sizeof(struct modem_message_node)*MAX_MSG_NODE_NUM, GFP_KERNEL); + if (msg_nodes == NULL) + return -ENOMEM; + + atomic_set(&nodes_count,0); + for (i=0; i<MAX_MSG_NODE_NUM; i++) { + msg_nodes[i].buno = i; + } + lost_msg_count = 0; + atomic_set(&nodes_count,i); + return 0; +} + +static struct modem_message_node * find_msg_node(void) { + unsigned long flags; + int i; + + if(atomic_read(&nodes_count) == 0) + return NULL; + //local_irq_save(flags); + spin_lock_irqsave(&int_lock, flags); + for (i=0; i<MAX_MSG_NODE_NUM; i++) { + if ((msg_nodes[i].buno & BUSY_BIT )==0) { + atomic_dec(&nodes_count); + msg_nodes[i].buno |= BUSY_BIT; + break; + } + } + //local_irq_restore(flags); + spin_unlock_irqrestore(&int_lock, flags); + if(i==MAX_MSG_NODE_NUM) + return NULL; + return &msg_nodes[i]; +} + +static void free_msg_node(struct modem_message_node *msg) +{ + if (msg) { + msg->buno &= ~BUSY_BIT; + atomic_inc(&nodes_count); + } +} + +static struct modem_message_node * modem_intf_get_message( void ) { + struct list_head *entry; + struct modem_message_node *msg; + unsigned long flags; + do { + if(!list_empty(&msg_list)) + break; + down(&msg_list_sem); + } while(1); + + if(!list_empty(&msg_list)) { + entry = msg_list.next; + //local_irq_save(flags); + spin_lock_irqsave(&int_lock, flags); + list_del_init(entry); + //local_irq_restore(flags); + spin_unlock_irqrestore(&int_lock, flags); + msg = (struct modem_message_node *)list_entry(entry,struct modem_message_node,link); + return (struct modem_message_node *)msg; + } + return NULL; +} + +static void modem_send_message( struct modem_message_node *msg ) +{ + unsigned long flags; + + wake_lock_timeout(&s_modem_intf_msg_wake_lock, HZ / 2); + //local_irq_save(flags); + spin_lock_irqsave(&int_lock, flags); + list_add_tail(&msg->link,&msg_list); + //local_irq_restore(flags); + spin_unlock_irqrestore(&int_lock, flags); + up(&msg_list_sem); +} + +static int modem_protocol_thread(void *data) +{ + struct modem_message_node *msg; + + int boot_cpu = smp_processor_id(); + long rc; + struct sched_param param = {.sched_priority = 98}; + + current_thread_info()->cpu = boot_cpu; + rc = sched_setaffinity(current->pid, cpumask_of(boot_cpu)); + + sched_setscheduler(current, SCHED_FIFO, ¶m); + + while(1) { + msg = modem_intf_get_message(); + if(msg == NULL) + continue; + wake_lock(&s_modem_intf_proc_wake_lock); + switch(modem_intf_device->mode) { + case MODEM_MODE_SHUTDOWN: + shutdown_protocol(msg); + break; + case MODEM_MODE_BOOT: + boot_protocol(msg); + break; + case MODEM_MODE_BOOTCOMP: + bootcomp_protocol(msg); + break; + case MODEM_MODE_NORMAL: + normal_protocol(msg); + break; + case MODEM_MODE_DUMP: + dump_memory_protocol(msg); + break; + case MODEM_MODE_RESET: + reset_protocol(msg); + break; + default: + break; + } + free_msg_node(msg); + wake_unlock(&s_modem_intf_proc_wake_lock); + } + return 0; +} + +int modem_intf_open(enum MODEM_Mode_type mode,int index) +{ + int retval=0; + + if(modem_intf_device == NULL) + return -1; + printk("boot gpio = %d\n",modem_intf_device->modem_config.modem_boot_gpio); + if(modem_intf_device->op == NULL) { + modem_intf_device->op = modem_intf_get_device_operation(modem_intf_device->modem_config.dev_type); + printk("modem_intf_open: op = %x\n",modem_intf_device->op); + if(modem_intf_device->op == NULL) + return -1; + } + + if((modem_intf_device->op)&&(mode == MODEM_MODE_BOOT)) { + if(0 != modem_intf_device->op->open(NULL)) { + return -1; + } + } + + return retval; +} + +int modem_intf_send_bootcomp_message(enum MODEM_Mode_type mode,int index) +{ + struct modem_message_node *msg; + + msg = find_msg_node(); + if (msg == NULL) { + lost_msg_count++; + printk("modem_intf_send_bootcomp_message MSG LOST!!!!!!\n"); + return -1; + } + msg->src = SRC_DLOADER; + msg->parameter1 = (int)modem_intf_device; + msg->parameter2 = 0; + msg->type = MODEM_BOOT_CMP; + + modem_send_message(msg); + + return 0; +} + +int modem_intf_read(char *buffer, int size,int index) +{ + int read_len; + + if(modem_intf_device->mode == MODEM_MODE_UNKNOWN) + return 0; + + read_len = pingpang_buffer_read(&modem_intf_device->recv_buffer,buffer,size); + return read_len; +} + +int modem_intf_write(char *buffer, int size,int index) +{ + struct modem_message_node *msg; + if(modem_intf_device->mode == MODEM_MODE_UNKNOWN) + return 0; + msg = find_msg_node(); + if (msg == NULL) { + lost_msg_count++; + return -1; + } + if(buffer && size ) + pingpang_buffer_write(&modem_intf_device->send_buffer,buffer,size); + msg->type = MODEM_TRANSFER_REQ; + msg->parameter1 = (int)modem_intf_device; + msg->parameter2 = 0; + modem_send_message(msg); + return size; +} + +void modem_intf_set_mode(enum MODEM_Mode_type mode,int force) +{ + + printk(KERN_INFO "modem_intf_set_mode enter old:%d,new:%d\n", + modem_intf_device->mode, mode); + + if(modem_intf_device->mode == mode) { + goto fin; + } + + if(force) { + modem_intf_device->mode = mode; + goto fin; + } + + switch(modem_intf_device->mode) { + case MODEM_MODE_BOOT: + if(MODEM_MODE_NORMAL == mode || MODEM_MODE_BOOTCOMP == mode) { + modem_intf_device->mode = mode; + } else { + printk(KERN_ERR "modem_intf: try to enter mode:%d, in MODEM_MODE_BOOT", mode); + } + break; + case MODEM_MODE_BOOTCOMP: + if(MODEM_MODE_NORMAL == mode) { + modem_intf_device->mode = mode; + } else { + printk(KERN_ERR "modem_intf: try to enter mode:%d, in MODEM_MODE_BOOTCOMP", mode); + } + break; + case MODEM_MODE_NORMAL: + if(MODEM_MODE_DUMP == mode || MODEM_MODE_RESET == mode) { + modem_intf_device->mode = mode; + } else { + printk(KERN_ERR "modem_intf: try to enter mode:%d, in MODEM_MODE_NORMAL", mode); + } + break; + case MODEM_MODE_DUMP: + if(MODEM_MODE_RESET == mode || MODEM_MODE_BOOT == mode) { + modem_intf_device->mode = mode; + } else { + printk(KERN_ERR "modem_intf: try to enter mode:%d, in MODEM_MODE_DUMP", mode); + } + break; + default: //MODEM_MODE_RESET + if(MODEM_MODE_BOOT == mode) { + modem_intf_device->mode = mode; + } else { + printk(KERN_ERR "modem_intf: try to enter mode:%d, in MODEM_MODE_RESET", mode); + } + break; + } + +fin: + printk(KERN_INFO "modem_intf_set_mode leave new:%d\n", modem_intf_device->mode); +} + +void modem_intf_send_channel_message(int dir,int para,int index) +{ + struct modem_message_node *msg; + + if(modem_intf_device->mode == MODEM_MODE_UNKNOWN) + return ; + if(modem_intf_device->mode != MODEM_MODE_NORMAL) + return; + msg = find_msg_node(); + if (msg == NULL) { + lost_msg_count++; + return ; + } + + msg->src = SRC_SPI; + msg->parameter1 = (int)modem_intf_device; + msg->parameter2 = dir; + msg->type = MODEM_TRANSFER_END; + modem_send_message(msg); +} + +void modem_intf_send_ctrl_gpio_message(enum MSG_CTRL_GPIO_TYPE gpio_type, int gpio_value) +{ + struct modem_message_node *msg; + + printk("ctrl_gpio: gpio_type = %d value = %d\n", gpio_type, gpio_value); + + if(modem_gpio_status[gpio_type] == gpio_value) { + return; + } + + modem_gpio_status[gpio_type] = gpio_value; + + //ctrl gpio only effective in these modem mode + if(modem_intf_device->mode != MODEM_MODE_BOOT && + modem_intf_device->mode != MODEM_MODE_BOOTCOMP && + modem_intf_device->mode != MODEM_MODE_NORMAL && + modem_intf_device->mode != MODEM_MODE_DUMP) { + + return; + } + + msg = find_msg_node(); + if (msg == NULL) { + lost_msg_count++; + printk("modem_intf_send_ctrl_gpio_message MSG LOST!!!!!!\n"); + return ; + } + msg->src = SRC_GPIO; + msg->parameter1 = (int)modem_intf_device; + msg->parameter2 = ((gpio_type << 16) | gpio_value); + msg->type = MODEM_CTRL_GPIO_CHG; + + modem_send_message(msg); +} + +void modem_intf_send_user_req_message(int req) +{ + struct modem_message_node *msg; + +retry: + msg = find_msg_node(); + if (msg == NULL) { + lost_msg_count++; + printk("modem_intf_send_user_reboot_req_message MSG LOST!!!!!!\n"); + + msleep(100); + printk("modem_intf_send_user_reboot_req_message retry!!!!!!\n"); + goto retry; + } + msg->src = SRC_USER; + msg->parameter1 = (int)modem_intf_device; + msg->parameter2 = req; + msg->type = MODEM_USER_REQ; + + modem_send_message(msg); +} + + +int modem_intf_send_boot_message() +{ + struct modem_message_node *msg; + +retry: + msg = find_msg_node(); + if (msg == NULL) { + lost_msg_count++; + printk("modem_intf_send_boot_message MSG LOST retry!!!!!!\n"); + + msleep(100); + goto retry; + } + msg->src = SRC_DLOADER; + msg->parameter1 = (int)modem_intf_device; + msg->parameter2 = MODEM_MODE_BOOT; + msg->type = MODEM_SET_MODE; + + modem_send_message(msg); + + return 0; +} + +int modem_intf_send_shutdown_message() +{ + struct modem_message_node *msg; + + msg = find_msg_node(); + if (msg == NULL) { + lost_msg_count++; + printk("modem_intf_send_shutdown_message MSG LOST !!!!!!\n"); + + return 0; + } + msg->src = SRC_DLOADER; + msg->parameter1 = (int)modem_intf_device; + msg->parameter2 = MODEM_MODE_SHUTDOWN; + msg->type = MODEM_SET_MODE; + + modem_send_message(msg); + + return 0; +} + + + + +void modem_intf_send_GPIO_message(int gpio_no,int status,int index) +{ + struct modem_message_node *msg; + + printk("GPIO_MSG: mode = %d gpio=%d status = %d\n",modem_intf_device->mode,gpio_no,status); + if(modem_intf_device->mode == MODEM_MODE_UNKNOWN) + return ; + msg = find_msg_node(); + if (msg == NULL) { + lost_msg_count++; + printk("modem_intf_send_GPIO_message MSG LOST!!!!!!\n"); + return ; + } + msg->src = SRC_GPIO; + msg->parameter1 = (int)modem_intf_device; + msg->parameter2 = gpio_no|(status << 16); + if(modem_intf_device->mode == MODEM_MODE_NORMAL) { + if (gpio_no == modem_intf_device->modem_config.modem_boot_gpio) { + msg->type = MODEM_SLAVE_RDY; + } else if (gpio_no == modem_intf_device->modem_config.modem_crash_gpio) { + msg->type = MODEM_SLAVE_RTS; + } else if (gpio_no == modem_intf_device->modem_config.modem_alive_gpio) { + msg->type = MODEM_SLAVE_RTS; + } + } else if(modem_intf_device->mode == MODEM_MODE_BOOT) { + if ((gpio_no == modem_intf_device->modem_config.modem_boot_gpio)) { + if ( status != 0) + msg->type = MODEM_SLAVE_RTS; + else + msg->type = MODEM_TRANSFER_END; + } + } else if(modem_intf_device->mode == MODEM_MODE_DUMP) { + if (gpio_no == modem_intf_device->modem_config.modem_crash_gpio) { + msg->type = MODEM_SLAVE_RTS; + } + } else { + free_msg_node(msg); + return; + } + modem_send_message(msg); +} +static int modem_intf_driver_remove( struct platform_device *_dev) +{ + return 0; +} + +/** + * This function shows the modem state + */ +static ssize_t modem_intf_state_show(struct device *dev,struct device_attribute *attr, char *buf) +{ + int ret; + printk("call modem_intf_state_show\n"); + ret = down_interruptible(&modem_event_sem); + if(ret == -EINTR) + return ret; + printk("event available:%d ...\n",modem_event); + snprintf(buf,4,"%d\n",modem_event); + printk("event = %s\n",buf); + return strlen(buf); +} + +extern void mux_ipc_enable(u8 is_enable); + + +void modem_intf_send_event(int event) +{ + //user space process should wait for modem_event_sem + wake_lock_timeout(&s_modem_intf_event_wake_lock, HZ / 2); + modem_event = event; + up(&modem_event_sem); +} + +void modem_intf_cp_reset_req_routine() +{ + modem_intf_send_event(MODEM_INTF_EVENT_CP_REQ_RESET); +} + + +void modem_intf_reboot_routine() +{ + //disable mux sdio spi + mux_ipc_enable(0); + sdio_ipc_enable(0); + modemsts_change_notification(MODEM_STATUS_REBOOT); + //modem_share_gpio_init(&(modem_intf_device->modem_config)); + + modem_intf_set_mode(MODEM_MODE_RESET, 0); + + modem_intf_send_event(MODEM_INTF_EVENT_FORCE_RESET); +} + +void modem_intf_alive_routine() +{ + //msleep(10*1000); + modem_intf_set_mode(MODEM_MODE_NORMAL, 0); + modem_share_gpio_uninit(&(modem_intf_device->modem_config)); + sdio_ipc_enable(1); + modemsts_change_notification(MODEM_STATUS_ALVIE); + mux_ipc_enable(1); + modem_intf_send_event(MODEM_INTF_EVENT_ALIVE); +} + +void modem_intf_assert_routine() +{ + modemsts_change_notification(MODEM_STATUS_ASSERT); + modem_intf_send_event(MODEM_INTF_EVENT_ASSERT); +} + +void modem_intf_wdg_reset_routine(void) +{ + modem_intf_send_event(MODEM_INTF_EVENT_WDG_RESET); +} + +int chk_watchdog_reset() +{ + unsigned long now = jiffies; + + do { + udelay(10); + if(1 == modem_intf_get_watchdog_gpio_value()) + { + return 1; + } + + }while((msecs_to_jiffies(jiffies - now) < MAX_WAIT_WATCHDOG_TIME)); + + return 0; +} +void modem_intf_ctrl_gpio_handle_normal(int status) +{ + int type, value; + + type = status >> 16; + value = status & 0xFFFF; + + switch(type) { + case GPIO_RESET: + //reset -> 1, ,means need reboot + if(value) { + //modem_intf_assert_routine(); + modem_intf_set_mode(MODEM_MODE_DUMP, 0); + modem_intf_cp_reset_req_routine(); + } + break; + case GPIO_ALIVE: + //alive -> 0,means assert + if(!value) { + //first check if watch dog reset changes cp_alive + if(chk_watchdog_reset()) { + printk("modem_intf: found watchdog reset in GPIO_ALIVE handle\n"); + modem_intf_wdg_reset_routine(); + modem_intf_set_mode(MODEM_MODE_DUMP, 0); + break; + } + modem_intf_assert_routine(); + modem_intf_set_mode(MODEM_MODE_DUMP, 0); + } + break; + case GPIO_WATCHDOG: + //watchdog -> 0,means watch dog reset + if(value) { + modem_intf_wdg_reset_routine(); + modem_intf_set_mode(MODEM_MODE_DUMP, 0); + } + break; + default: + break; + } + +} + +void modem_intf_ctrl_gpio_handle_dump(int status) +{ + int type, value; + + type = status >> 16; + value = status & 0xFFFF; + + switch(type) { + case GPIO_RESET: + //reset -> 1,means need reboot + if(value) { + modem_intf_cp_reset_req_routine(); + } + break; + case GPIO_ALIVE: + + break; + case GPIO_WATCHDOG: + + break; + default: + break; + } + +} + +void modem_intf_ctrl_gpio_handle_boot(int status) +{ + int type, value; + + type = status >> 16; + value = status & 0xFFFF; + + switch(type) { + case GPIO_RESET: + break; + case GPIO_ALIVE: + //alive -> 1, means alive + if(value) { + modem_intf_alive_routine(); + } + break; + case GPIO_WATCHDOG: + break; + default: + break; + } + +} + +void modem_intf_ctrl_gpio_handle_bootcomp(int status) +{ + int type, value; + + type = status >> 16; + value = status & 0xFFFF; + + switch(type) { + case GPIO_RESET: + break; + case GPIO_ALIVE: + //alive -> 1, means alive + if(value) { + modem_intf_alive_routine(); + } + break; + case GPIO_WATCHDOG: + break; + default: + break; + } + +} + + +static ssize_t modem_intf_state_store(struct device *dev,struct device_attribute *attr, const char *buf, size_t size) +{ + int status; + + printk("modem_intf_state_store :%d : %s\n",modem_gpio_status,buf); + status = simple_strtoul(buf, NULL, 16); + //status 0, mean enter boot mode + if(0 == status) { + //status 0 means start to boot cp + modem_share_gpio_init(&(modem_intf_device->modem_config)); + + printk("modem_intf_open: modem_intf_send_boot_message mode = %d\n", MODEM_MODE_BOOT); + modem_intf_send_boot_message(); + } else { + modem_intf_send_user_req_message(status); + } + + return 0; +} + +DEVICE_ATTR(state, S_IRUGO | S_IWUSR, modem_intf_state_show,modem_intf_state_store); + +static int s_modem_poweron = 0; + +void on_modem_poweron() +{ + modem_gpio_irq_init(&(modem_intf_device->modem_config)); + modemsts_change_notification(MODEM_STATUS_POWERON); +} + +static ssize_t modem_intf_modempower_show(struct device *dev,struct device_attribute *attr, char *buf) +{ + int count=0; + + if(buf == NULL) + return 0; + printk("modempower_show: %d\n",s_modem_poweron); + + if(s_modem_poweron == 0) { + snprintf(buf,4,"off"); + count = 4; + } else { + snprintf(buf,3,"on"); + count = 3; + } + return count; +} + +static ssize_t modem_intf_modempower_store(struct device *dev,struct device_attribute *attr, const char *buf, size_t size) +{ + int power; + + if(buf == NULL) + return 0; + power = simple_strtoul(buf, NULL, 16); + if (power == 1) { + if(s_modem_poweron) + return size; + modem_poweron(); + s_modem_poweron = 1; + on_modem_poweron(); + } else if(power == 0) { + if(!s_modem_poweron) + return size; + modem_poweroff(); + s_modem_poweron = 0; + } + return size; +} + +DEVICE_ATTR(modempower, S_IRUGO | S_IWUSR, modem_intf_modempower_show,modem_intf_modempower_store); + +static void modem_intf_init_poweron_timeout(unsigned long priv) +{ + modem_poweron_async_step2(); + s_modem_poweron = 1; + on_modem_poweron(); +} + +static void modem_intf_init_poweron_modem() +{ + init_timer(&s_modem_watch_timer); + s_modem_watch_timer.expires = jiffies + (2 * HZ); + s_modem_watch_timer.data = (unsigned long)NULL; + s_modem_watch_timer.function = modem_intf_init_poweron_timeout; + + modem_poweron_async_step1(); + + add_timer(&s_modem_watch_timer); +} + + +static ssize_t modem_intf_modemreset_store(struct device *dev,struct device_attribute *attr, const char *buf, size_t size) +{ + int power; + + if(buf == NULL) + return 0; + if(!s_modem_poweron) + return -1; + power = simple_strtoul(buf, NULL, 16); + if (power == 1) { + modem_soft_reset(); + } else if(power == 2) { + modem_hard_reset(); + } + return size; +} + +DEVICE_ATTR(modemreset, S_IWUSR, NULL,modem_intf_modemreset_store); + +static int modem_intf_driver_probe(struct platform_device *_dev) +{ + int retval = 0; + int i; + struct modem_intf_platform_data *modem_config = _dev->dev.platform_data; + struct modem_intf_device *device; + + dev_dbg(&_dev->dev, "MODEM_INTF_driver_probe(%p)\n", _dev); + + device = kzalloc(sizeof(*device), GFP_KERNEL); + + if (!device) { + printk("MODEM device alloc memory failed!!!\n"); + retval = -ENOMEM; + goto fail; + } + + memcpy(&device->modem_config,modem_config,sizeof(*modem_config)); + device->send_buffer.type = BUF_SEND; + device->recv_buffer.type = BUF_RECV; + if (pingpang_buffer_init(&device->send_buffer)) { + dev_dbg(&_dev->dev, "send_buffer init failed\n"); + retval = -ENOMEM; + goto fail; + } + + if (pingpang_buffer_init(&device->recv_buffer)) { + dev_dbg(&_dev->dev, "receive_buffer init failed\n"); + pingpang_buffer_free(&device->send_buffer); + retval = -ENOMEM; + goto fail; + } + // sprd_greg_set_bits(REG_TYPE_GLOBAL, 0x00000040, GR_PIN_CTL); + device->mode = MODEM_MODE_SHUTDOWN; + modem_intf_device = device; + modem_intf_device->op = NULL; + + for(i = 0; i < CTRL_GPIO_MAX; i++) { + modem_gpio_status[i] = 0xFF; + } + retval = device_create_file(&_dev->dev, &dev_attr_state); + retval = device_create_file(&_dev->dev, &dev_attr_modempower); + retval = device_create_file(&_dev->dev, &dev_attr_modemreset); + modem_gpio_init(&modem_intf_device->modem_config); + modem_intf_register_device_operation(modem_sdio_drv_init()); + modem_event = MODEM_INTF_EVENT_SHUTDOWN; + sema_init(&modem_event_sem,1); + spin_lock_init(&int_lock); + modem_intf_init_poweron_modem(); + return 0; +fail: + kfree(device); + modem_intf_device = NULL; + return retval; +} + +static void modem_intf_driver_shutdown(struct platform_device *_dev) +{ + unsigned long timeout = jiffies + 30 * HZ; + + //wait downloading cp finished + printk(KERN_ERR "modem_intf: modem_intf_driver_shutdown enter!\n" ); + while(time_after(timeout, jiffies)) { + if(modem_intf_device->mode == MODEM_MODE_BOOT) + { + printk(KERN_ERR "modem_intf: shutdown found booting cp ...!\n" ); + msleep(1000); + } else { + break; + } + } + + //send message + printk(KERN_ERR "modem_intf: shutdown modem_intf_send_shutdown_message!\n" ); + modem_intf_send_shutdown_message(); + + //power off modem + printk(KERN_ERR "modem_intf: shutdown modem_poweroff!\n" ); + modem_poweroff(); +} + + +static struct platform_driver modem_intf_driver = { + .driver = { + .name = "modem_interface", + .owner = THIS_MODULE, + }, + + .probe = modem_intf_driver_probe, + .remove = modem_intf_driver_remove, + .shutdown = modem_intf_driver_shutdown, + +}; +static int __init init(void) +{ + int retval = 0; + struct task_struct * task; + printk(KERN_INFO "MODEM_INF_DRIVER: version 0.1\n" ); + + //init_waitqueue_head(&mdoem_mode_normal); + sema_init(&msg_list_sem,0); + retval = init_msg_nodes(); + retval = platform_driver_probe(&modem_intf_driver, modem_intf_driver_probe); + if (retval < 0) { + printk(KERN_ERR "%s retval=%d\n", __func__, retval); + } + if(retval < 0) + return retval; + + wake_lock_init(&s_modem_intf_proc_wake_lock, WAKE_LOCK_SUSPEND, "modem_if_proc_lock"); + wake_lock_init(&s_modem_intf_msg_wake_lock, WAKE_LOCK_SUSPEND, "modem_if_msg_lock"); + wake_lock_init(&s_modem_intf_event_wake_lock, WAKE_LOCK_SUSPEND, "modem_if_evt_lock"); + task = kthread_create(modem_protocol_thread, NULL, "ModemIntf"); + if(0 != task) + wake_up_process(task); + return retval; +} +module_init(init); + +static void __exit cleanup(void) +{ + printk(KERN_DEBUG "MODEM_INTF_driver_cleanup()\n"); + + if (modem_intf_device) { + pingpang_buffer_free(&modem_intf_device->send_buffer); + pingpang_buffer_free(&modem_intf_device->recv_buffer); + kfree(modem_intf_device); + modem_intf_device = NULL; + } + platform_driver_unregister(&modem_intf_driver); + + printk(KERN_INFO "MODEM_INTF module removed\n" ); +} +module_exit(cleanup); diff --git a/drivers/char/modem_interface/modem_interface_driver.h b/drivers/char/modem_interface/modem_interface_driver.h new file mode 100644 index 00000000..786b81ae --- /dev/null +++ b/drivers/char/modem_interface/modem_interface_driver.h @@ -0,0 +1,84 @@ +#ifndef __MODEM_INTF_DRIVER_H__ +#define __MODEM_INTF_DRIVER_H__ +#include <mach/modem_interface.h> + + +#define MODEM_INTF_EVENT_ASSERT 0 +#define MODEM_INTF_EVENT_ALIVE 1 +#define MODEM_INTF_EVENT_CP_REQ_RESET 2 +#define MODEM_INTF_EVENT_SHUTDOWN 4 +#define MODEM_INTF_EVENT_FORCE_RESET 8 +#define MODEM_INTF_EVENT_WDG_RESET 16 + + + + + +enum TRAN_DIR_t{ + TRAN_DIR_IN, + TRAN_DIR_OUT, + TRAN_DIR_NONE, +}; + +enum MSG_SRC_type { + SRC_GPIO, + SRC_SPI, + SRC_MUX, + SRC_DLOADER, + SRC_USER +}; + +enum MODEM_MSG_type { + MODEM_TRANSFER_REQ, // high layer(mux /download driver) request to send. + MODEM_TRANSFER_END,// transfer complete + MODEM_SLAVE_RTS, // MODEM request to send data + MODEM_SLAVE_RDY, // indicate MODEM is ready to receive. + MODEM_SET_MODE, + MODEM_CTRL_GPIO_CHG, + MODEM_USER_REQ, + MODEM_BOOT_CMP, + MODEM_OPEN_DEVICE +}; + +enum MSG_CTRL_GPIO_TYPE { + GPIO_ALIVE = 0, + GPIO_RESET, + GPIO_WATCHDOG, + CTRL_GPIO_MAX +}; + + +struct modem_message_node{ + struct list_head link; + int buno; + enum MSG_SRC_type src; + enum MODEM_MSG_type type; + int parameter1; + int parameter2; +}; + +struct modem_device_operation { + enum MODEM_device_type dev; + int (*open) (void*para); + int (*write)(char *buffer, int size); + int (*read)(char *buffer, int size); +}; + +typedef struct modem_intf_device { + struct modem_intf_platform_data modem_config; + enum MODEM_Mode_type mode; + struct modem_device_operation *op; + struct modem_buffer send_buffer; + struct modem_buffer recv_buffer; + enum TRAN_DIR_t direction; + int buffer_index; + int status; + int out_transfering; + int out_transfer_pending; +} MODEM_DEV_T; + +extern void boot_protocol(struct modem_message_node *msg); +extern void normal_protocol(struct modem_message_node *msg); +extern void dump_memory_protocol(struct modem_message_node *msg); +extern char *modem_intf_msg_string(enum MODEM_MSG_type msg); +#endif diff --git a/drivers/char/modem_interface/modem_sdio_drv.c b/drivers/char/modem_interface/modem_sdio_drv.c new file mode 100644 index 00000000..efe7288b --- /dev/null +++ b/drivers/char/modem_interface/modem_sdio_drv.c @@ -0,0 +1,50 @@ +/* + * kernel/driver/char/modem_interface/modem_sdio_drv.c + * + * Generic modem interface sdio API + * + * Author: Jiayong Yang(Jiayong.Yang@spreadtrum.com) + * Created: Jul 27, 2012 + * Copyright: Spreadtrum Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + + +#include <linux/mutex.h> +#include <linux/dma-mapping.h> +#include <linux/proc_fs.h> +#include "modem_buffer.h" +#include "modem_interface_driver.h" +extern struct spi_device *sprd_spi_modem_device_register(int master_bus_num,struct spi_board_info *chip); + +extern int sprd_sdio_channel_open(void); +extern void sprd_sdio_channel_close(void); +extern int sprd_sdio_channel_rx(char *buf, int len, unsigned int addr); +extern int sprd_sdio_channel_tx(const char *buf, unsigned int len, unsigned int addr); + +static int modem_sdio_open(void *para) +{ + return sprd_sdio_channel_open(); +} +int modem_sdio_read(char *buffer,int size) +{ + return sprd_sdio_channel_rx(buffer,size, 0); +} +int modem_sdio_write(char *buffer,int size) +{ + return sprd_sdio_channel_tx((const char *)buffer,size, 0); +} + +static struct modem_device_operation sdio_device_op = { + .dev = MODEM_DEV_SDIO, + .open = modem_sdio_open, + .read = modem_sdio_read, + .write =modem_sdio_write +}; +struct modem_device_operation * modem_sdio_drv_init(void) +{ + return &sdio_device_op; +} diff --git a/drivers/char/modem_interface/modem_spi_drv.c b/drivers/char/modem_interface/modem_spi_drv.c new file mode 100644 index 00000000..83573ed8 --- /dev/null +++ b/drivers/char/modem_interface/modem_spi_drv.c @@ -0,0 +1,124 @@ +/* + * kernel/driver/char/modem_interface/modem_spi_drv.c + * + * Generic modem interface spi API. + * + * Author: Jiayong Yang(Jiayong.Yang@spreadtrum.com) + * Created: Jul 27, 2012 + * Copyright: Spreadtrum Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/init.h> +#include <linux/module.h> +#include <linux/ioctl.h> +#include <linux/fs.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/list.h> +#include <linux/errno.h> +#include <linux/mutex.h> +#include <linux/slab.h> +#include <linux/wait.h> +#include <linux/sched.h> +#include <linux/kthread.h> +#include <linux/spi/spi.h> +#include <linux/io.h> +#include <linux/dma-mapping.h> +#include <linux/version.h> +#include <linux/proc_fs.h> +#include <linux/platform_device.h> +#include <linux/gpio.h> +#include <linux/interrupt.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include "modem_buffer.h" +#include "modem_interface_driver.h" + +extern struct spi_device *sprd_spi_modem_device_register(int master_bus_num,struct spi_board_info *chip); + +dma_addr_t spi2_rx_buf_dma_phys; +dma_addr_t spi2_tx_buf_dma_phys; +void *spi2_rx_buf_dma_virt; +void *spi2_tx_buf_dma_virt; + +spinlock_t write_spinlock; +#define SPI_WRITE_DEBUG +#ifdef SPI_WRITE_DEBUG +spinlock_t log_spinlock; +#endif + +static struct spi_device *spi_g=NULL; + +static int modem_spi_open(void *para) +{ + return 0; +} +int modem_spi_read(char *buffer,int size) +{ + spi_read(spi_g,buffer,size); +} +int modem_spi_write(char *buffer,int size) +{ + unsigned short *data = buffer; + spi_write(spi_g,buffer,size); +} + +static int mux2spi_probe(struct spi_device *spi) +{ + int status; + + spi_g = spi; + spi_g->mode = SPI_MODE_1; + spi_g->max_speed_hz = 1000000; + spi_g->bits_per_word = 32; + + spi2_rx_buf_dma_virt =dma_alloc_coherent(NULL, 4096,&spi2_rx_buf_dma_phys, GFP_KERNEL | GFP_DMA); + + if (spi2_rx_buf_dma_virt == NULL){ + printk(KERN_ERR "spi2_rx_buf_dma_virt alloc fail"); + return (-ENOMEM); + } + + spi2_tx_buf_dma_virt =dma_alloc_coherent(NULL, 4096,&spi2_tx_buf_dma_phys, GFP_KERNEL | GFP_DMA); + + if (spi2_tx_buf_dma_virt == NULL){ + printk(KERN_ERR "spi2_tx_buf_dma_virt alloc fail"); + return (-ENOMEM); + } + + spin_lock_init(&write_spinlock); +#ifdef SPI_WRITE_DEBUG + spin_lock_init(&log_spinlock); +#endif + status = spi_setup(spi_g); + + ret: + return status; +} + +static struct spi_driver mux_spi_drv = { + .driver = { + .name = "spi_mux", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + .probe = mux2spi_probe, +}; + + +static struct modem_device_operation spi_device_op = { + .dev = MODEM_DEV_SPI, + .open = modem_spi_open, + .read = modem_spi_read, + .write =modem_spi_write +}; +struct modem_device_operation * modem_spi_drv_init(void) +{ + //sprd_spi_modem_device_register(0, NULL); + spi_g = NULL; + spi_register_driver(&mux_spi_drv); + return &spi_device_op; +} diff --git a/drivers/char/modem_interface/normal_mode.c b/drivers/char/modem_interface/normal_mode.c new file mode 100644 index 00000000..5ab8fb5e --- /dev/null +++ b/drivers/char/modem_interface/normal_mode.c @@ -0,0 +1,157 @@ +/* + * kernel/driver/char/modem_interface/normal_mode.c + * + * Generic modem interface GPIO handshake and dump memory process. + * + * Author: Jiayong Yang(Jiayong.Yang@spreadtrum.com) + * Created: Jul 27, 2012 + * Copyright: Spreadtrum Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/init.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/semaphore.h> +#include <linux/delay.h> +#include "modem_buffer.h" +#include "modem_interface_driver.h" + +extern size_t Read_modem_data_ext(const unsigned char * buf, unsigned short len); + +extern void modem_intf_set_mode(enum MODEM_Mode_type mode,int force); +extern void modem_intf_ctrl_gpio_handle_bootcomp(int status); +extern void modem_intf_ctrl_gpio_handle_dump(int status); +extern void modem_intf_ctrl_gpio_handle_normal(int status); +extern void modem_intf_reboot_routine(void); + + +void shutdown_protocol(struct modem_message_node *msg) +{ + struct modem_intf_device * device; + + if((msg == NULL)||(msg->parameter1==0)) + return; + + printk(KERN_INFO ">>shutdown entry message: %s \n",modem_intf_msg_string(msg->type)); + + device = (struct modem_intf_device *)msg->parameter1; + + switch(msg->type) { + case MODEM_SET_MODE: + modem_intf_set_mode(msg->parameter2, 1); + device->status = 0; + device->out_transfering = 0; + break; + default: + break; + } +} + +void bootcomp_protocol(struct modem_message_node *msg) +{ + struct modem_intf_device * device; + + if((msg == NULL)||(msg->parameter1==0)) + return; + + printk(KERN_INFO ">>bootcomp entry message: %s \n",modem_intf_msg_string(msg->type)); + + device = (struct modem_intf_device *)msg->parameter1; + + switch(msg->type) { + case MODEM_CTRL_GPIO_CHG: + modem_intf_ctrl_gpio_handle_bootcomp(msg->parameter2); + break; + case MODEM_USER_REQ: + modem_intf_reboot_routine(); + break; + case MODEM_SET_MODE: + modem_intf_set_mode(msg->parameter2, 1); + device->status = 0; + device->out_transfering = 0; + break; + default: + break; + } +} + + +void dump_memory_protocol(struct modem_message_node *msg) +{ + struct modem_intf_device * device; + + if((msg == NULL)||(msg->parameter1==0)) + return; + + printk(KERN_INFO ">>dump entry message: %s \n",modem_intf_msg_string(msg->type)); + + device = (struct modem_intf_device *)msg->parameter1; + + switch(msg->type) { + case MODEM_CTRL_GPIO_CHG: + modem_intf_ctrl_gpio_handle_dump(msg->parameter2); + break; + case MODEM_USER_REQ: + modem_intf_reboot_routine(); + break; + case MODEM_SET_MODE: + modem_intf_set_mode(msg->parameter2, 1); + device->status = 0; + device->out_transfering = 0; + break; + default: + break; + } +} + +void reset_protocol(struct modem_message_node *msg) +{ + struct modem_intf_device * device; + + if((msg == NULL)||(msg->parameter1==0)) + return; + + printk(KERN_INFO ">>reset entry message: %s \n",modem_intf_msg_string(msg->type)); + + device = (struct modem_intf_device *)msg->parameter1; + + switch(msg->type) { + case MODEM_SET_MODE: + modem_intf_set_mode(msg->parameter2, 1); + device->status = 0; + device->out_transfering = 0; + break; + default: + break; + } +} + +void normal_protocol(struct modem_message_node *msg) +{ + struct modem_intf_device * device; + if((msg == NULL)||(msg->parameter1==0)) + return; + + printk(KERN_INFO ">>normal entry message: %s \n",modem_intf_msg_string(msg->type)); + + device = (struct modem_intf_device *)msg->parameter1; + + switch(msg->type) { + case MODEM_CTRL_GPIO_CHG: + modem_intf_ctrl_gpio_handle_normal(msg->parameter2); + break; + case MODEM_USER_REQ: + modem_intf_reboot_routine(); + break; + case MODEM_SET_MODE: + modem_intf_set_mode(msg->parameter2, 1); + device->status = 0; + device->out_transfering = 0; + break; + default: + break; + } +} |
