diff options
Diffstat (limited to 'drivers/misc/modem_if')
32 files changed, 10642 insertions, 2597 deletions
diff --git a/drivers/misc/modem_if/Kconfig b/drivers/misc/modem_if/Kconfig index 94cb48c..6a6eeab 100644 --- a/drivers/misc/modem_if/Kconfig +++ b/drivers/misc/modem_if/Kconfig @@ -34,6 +34,16 @@ config CDMA_MODEM_MDM6600 depends on SEC_MODEM default n +config TDSCDMA_MODEM_SPRD8803 + bool "modem chip : SPRD SC8803" + depends on SEC_MODEM + default n + +config GSM_MODEM_ESC6270 + bool "modem chip : QC ESC6270" + depends on SEC_MODEM + default n + config LINK_DEVICE_MIPI bool "modem driver link device MIPI-HSI" depends on SEC_MODEM @@ -44,6 +54,10 @@ config LINK_DEVICE_DPRAM depends on SEC_MODEM default n +config LINK_DEVICE_PLD + bool "modem driver link device PLD" + depends on SEC_MODEM + default n config LINK_DEVICE_USB bool "modem driver link device USB" depends on SEC_MODEM @@ -59,6 +73,16 @@ config LINK_DEVICE_C2C depends on SEC_MODEM default n +config LINK_DEVICE_SPI + bool "modem driver link device SPI" + depends on SEC_MODEM + default n + +config WORKQUEUE_FRONT + bool "IPC: SPI workqueue front" + depends on SEC_MODEM + default n + config IPC_CMC22x_OLD_RFS bool "IPC: CMC22x ancient RFS" depends on SEC_MODEM @@ -73,3 +97,8 @@ config SIM_DETECT bool "SIM_DETECT pin" depends on SEC_MODEM default n + +config SIM_SLOT_SWITCH + bool "SIM_SLOT_SWITCH" + depends on SEC_MODEM + default n diff --git a/drivers/misc/modem_if/Makefile b/drivers/misc/modem_if/Makefile index dafc8c0..5bd62ea 100644 --- a/drivers/misc/modem_if/Makefile +++ b/drivers/misc/modem_if/Makefile @@ -4,7 +4,7 @@ EXTRA_CFLAGS += -Idrivers/misc/modem_if obj-y += sipc5_modem.o sipc5_io_device.o obj-y += sipc4_modem.o sipc4_io_device.o -obj-y += modem_net_flowcontrol_device.o modem_utils.o modem_debug.o +obj-y += modem_net_flowcontrol_device.o modem_utils.o obj-$(CONFIG_UMTS_MODEM_XMM6260) += modem_modemctl_device_xmm6260.o obj-$(CONFIG_UMTS_MODEM_XMM6262) += modem_modemctl_device_xmm6262.o @@ -12,9 +12,15 @@ obj-$(CONFIG_CDMA_MODEM_CBP71) += modem_modemctl_device_cbp71.o obj-$(CONFIG_CDMA_MODEM_CBP72) += modem_modemctl_device_cbp72.o obj-$(CONFIG_LTE_MODEM_CMC221) += modem_modemctl_device_cmc221.o lte_modem_bootloader.o obj-$(CONFIG_CDMA_MODEM_MDM6600) += modem_modemctl_device_mdm6600.o +obj-$(CONFIG_GSM_MODEM_ESC6270) += modem_modemctl_device_esc6270.o +obj-$(CONFIG_TDSCDMA_MODEM_SPRD8803) += modem_modemctl_device_sprd8803.o obj-$(CONFIG_LINK_DEVICE_MIPI) += modem_link_device_mipi.o -obj-$(CONFIG_LINK_DEVICE_DPRAM) += modem_link_device_dpram.o +obj-$(CONFIG_LINK_DEVICE_DPRAM) += modem_link_device_dpram.o modem_link_device_dpram_ext_op.o +obj-$(CONFIG_LINK_DEVICE_PLD) += modem_link_device_pld.o modem_link_device_pld_ext_op.o obj-$(CONFIG_LINK_DEVICE_USB) += modem_link_device_usb.o modem_link_pm_usb.o obj-$(CONFIG_LINK_DEVICE_HSIC) += modem_link_device_hsic.o obj-$(CONFIG_LINK_DEVICE_C2C) += modem_link_device_c2c.o +obj-$(CONFIG_LINK_DEVICE_SPI) += modem_link_device_spi.o + +obj-$(CONFIG_SIM_SLOT_SWITCH) += modem_sim_slot_switch.o diff --git a/drivers/misc/modem_if/modem_debug.c b/drivers/misc/modem_if/modem_debug.c deleted file mode 100644 index 1ad3073..0000000 --- a/drivers/misc/modem_if/modem_debug.c +++ /dev/null @@ -1,429 +0,0 @@ -/* linux/drivers/misc/modem_if/modem_debug.c - * - * Copyright (C) 2010 Samsung Electronics. - * - * 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/interrupt.h> -#include <linux/platform_device.h> -#include <linux/miscdevice.h> -#include <linux/if_arp.h> - -#include <linux/uaccess.h> -#include <linux/fs.h> -#include <linux/io.h> -#include <linux/wait.h> -#include <linux/time.h> -#include <linux/sched.h> -#include <linux/slab.h> -#include <linux/mutex.h> -#include <linux/irq.h> -#include <linux/gpio.h> -#include <linux/delay.h> -#include <linux/wakelock.h> - -#include <linux/platform_data/modem.h> -#include "modem_prj.h" -#include "modem_variation.h" -#include "modem_utils.h" - -static const char *hex = "0123456789abcdef"; - -static inline void mif_irq2str(struct mif_event_buff *evtb, char *buff) -{ - int i; - char tb[32]; - - if (evtb->link_type == LINKDEV_DPRAM) { - struct dpram_irq_buff *irqb = &evtb->dpram_irqb; - - sprintf(tb, "{0x%04X %d 0x%04X}", - irqb->magic, irqb->access, irqb->int2ap); - strcat(buff, tb); - - for (i = 0; i < IPC_RFS; i++) { - snprintf(tb, 32, " {%d: %u %u %u %u}", i, - irqb->qsp[i].txq.in, irqb->qsp[i].txq.out, - irqb->qsp[i].rxq.in, irqb->qsp[i].rxq.out); - strcat(buff, tb); - } - } else { - sprintf(tb, "link unspeicified"); - strcat(buff, tb); - } -} - -static inline void mif_dump2hex(const char *data, size_t len, char *buff) -{ - char *src = (char *)data; - int i; - char tb[4]; - - tb[3] = 0; - for (i = 0; i < len; i++) { - tb[0] = hex[(*src >> 4) & 0xf]; - tb[1] = hex[*src & 0xf]; - tb[2] = ' '; - strcat(buff, tb); - src++; - } -} - -static inline void mif_fin_str(char *buff) -{ - char tb[4]; - sprintf(tb, "\n"); - strcat(buff, tb); -} - -static void mif_log2str(struct mif_event_buff *evtb, char *buff) -{ - struct timeval *tv; - struct tm date; - - tv = &evtb->tv; - - time_to_tm((tv->tv_sec - sys_tz.tz_minuteswest * 60), 0, &date); - sprintf(evtb->time, "%02d:%02d:%02d.%03ld", - date.tm_hour, date.tm_min, date.tm_sec, (tv->tv_usec / 1000)); - - if (evtb->evt == MIF_IRQ_EVT) { - sprintf(buff, "%s IRQ <%s> ", evtb->time, evtb->ld); - mif_irq2str(evtb, buff); - mif_fin_str(buff); - } else { - size_t len = evtb->len < (MAX_MIF_LOG_LEN >> 3) ? - evtb->len : (MAX_MIF_LOG_LEN >> 3); - sprintf(buff, "%s [%d] <%s:%s> ", - evtb->time, evtb->evt, evtb->iod, evtb->ld); - mif_dump2hex(evtb->data, len, buff); - mif_fin_str(buff); - } -} - -static void mif_print_logs(struct modem_ctl *mc) -{ - struct sk_buff *skb; - struct mif_event_buff *evtb; - u8 *buff; - - buff = kmalloc(2048, GFP_ATOMIC); - if (!buff) - return; - - while (1) { - skb = skb_dequeue(&mc->evtq); - if (!skb) - break; - - evtb = (struct mif_event_buff *)skb->data; - memset(buff, 0, 2048); - - mif_log2str(evtb, buff); - pr_info("mif: %s", buff); - - dev_kfree_skb_any(skb); - } - - kfree(buff); -} - -static void mif_save_logs(struct modem_ctl *mc) -{ - struct file *fp = mc->log_fp; - struct sk_buff *skb; - struct mif_event_buff *evtb; - int qlen = mc->evtq.qlen; - int i; - int ret; - mm_segment_t old_fs; - - old_fs = get_fs(); - set_fs(get_ds()); - - for (i = 0; i < qlen; i++) { - skb = skb_dequeue(&mc->evtq); - -#if 0 - if (evtb->evt < mc->log_level) { - ret = fp->f_op->write(fp, skb->data, - MAX_MIF_EVT_BUFF_SIZE, &fp->f_pos); - if (ret < 0) { - mif_log2str((struct mif_event_buff *)skb->data, - mc->buff); - printk(KERN_ERR "%s", mc->buff); - } - } -#else - evtb = (struct mif_event_buff *)skb->data; - if (evtb->evt < mc->log_level) { - mif_log2str(evtb, mc->buff); - ret = fp->f_op->write(fp, mc->buff, strlen(mc->buff), - &fp->f_pos); - if (ret < 0) - printk(KERN_ERR "%s", mc->buff); - } -#endif - - dev_kfree_skb_any(skb); - } - - set_fs(old_fs); -} - -static void mif_evt_work(struct work_struct *work) -{ - struct modem_ctl *mc = container_of(work, struct modem_ctl, evt_work); - struct file *fp = mc->log_fp; - loff_t size; - mm_segment_t old_fs; - - /* use_mif_log */ - - if (!mc->log_level || mc->fs_failed) { - mif_print_logs(mc); - return; - } - - /* use_mif_log && log_level && !fs_failed */ - - if (!mc->fs_ready) { - if (mc->evtq.qlen > 1000) - mif_print_logs(mc); - return; - } - - /* use_mif_log && log_level && !fs_failed && fs_ready */ - - if (fp) { - mif_save_logs(mc); - - old_fs = get_fs(); - set_fs(get_ds()); - size = fp->f_pos; - set_fs(old_fs); - if (size > MAX_MIF_LOG_FILE_SIZE) { - mif_err("%s size %lld > %d\n", mc->log_path, size, - MAX_MIF_LOG_FILE_SIZE); - mif_close_log_file(mc); - mif_open_log_file(mc); - } - } -} - -void mif_irq_log(struct modem_ctl *mc, struct sk_buff *skb) -{ - if (!mc || !mc->use_mif_log) - return; - skb_queue_tail(&mc->evtq, skb); -} - -void mif_ipc_log(struct modem_ctl *mc, enum mif_event_id evt, - struct io_device *iod, struct link_device *ld, - u8 *data, unsigned size) -{ - struct sk_buff *skb; - struct mif_event_buff *evtb; - unsigned len; - - if (!mc || !mc->use_mif_log) - return; - - skb = alloc_skb(MAX_MIF_EVT_BUFF_SIZE, GFP_ATOMIC); - if (!skb) - return; - - evtb = (struct mif_event_buff *)skb_put(skb, MAX_MIF_EVT_BUFF_SIZE); - memset(evtb, 0, MAX_MIF_EVT_BUFF_SIZE); - - do_gettimeofday(&evtb->tv); - evtb->evt = evt; - - strncpy(evtb->mc, mc->name, MAX_MIF_NAME_LEN); - - if (iod) - strncpy(evtb->iod, iod->name, MAX_MIF_NAME_LEN); - - if (ld) { - strncpy(evtb->ld, ld->name, MAX_MIF_NAME_LEN); - evtb->link_type = ld->link_type; - } - - len = min_t(unsigned, MAX_MIF_LOG_LEN, size); - memcpy(evtb->data, data, len); - - evtb->rcvd = size; - evtb->len = len; - - skb_queue_tail(&mc->evtq, skb); -} - -void mif_flush_logs(struct modem_ctl *mc) -{ - if (!mc || !mc->use_mif_log) - return; - - if (atomic_read(&mc->log_open)) - queue_work(mc->evt_wq, &mc->evt_work); -} - -int mif_init_log(struct modem_ctl *mc) -{ - char wq_name[32]; - char wq_suffix[32]; - - mc->log_level = 0; - - atomic_set(&mc->log_open, 0); - - memset(wq_name, 0, sizeof(wq_name)); - memset(wq_suffix, 0, sizeof(wq_suffix)); - strncpy(wq_name, mc->name, sizeof(wq_name)); - snprintf(wq_suffix, sizeof(wq_suffix), "%s", "_evt_wq"); - strncat(wq_name, wq_suffix, sizeof(wq_suffix)); - mc->evt_wq = create_singlethread_workqueue(wq_name); - if (!mc->evt_wq) { - printk(KERN_ERR "<%s:%s> fail to create %s\n", - __func__, mc->name, wq_name); - return -EFAULT; - } - printk(KERN_ERR "<%s:%s> %s created\n", - __func__, mc->name, wq_name); - - INIT_WORK(&mc->evt_work, mif_evt_work); - - skb_queue_head_init(&mc->evtq); - - mc->buff = kzalloc(PAGE_SIZE, GFP_KERNEL); - if (!mc->buff) { - printk(KERN_ERR "<%s> kzalloc fail\n", __func__); - return -ENOMEM; - } - - return 0; -} - -void mif_set_log_level(struct modem_ctl *mc) -{ - struct file *fp; - int ret; - mm_segment_t old_fs; - - mc->log_level = 0; - - old_fs = get_fs(); - set_fs(get_ds()); - fp = filp_open(MIF_LOG_LV_FILE, O_RDONLY, 0); - if (IS_ERR(fp)) { - printk(KERN_ERR "<%s:%s> %s open fail\n", - __func__, mc->name, MIF_LOG_LV_FILE); - } else { - char tb; - - ret = fp->f_op->read(fp, &tb, 1, &fp->f_pos); - if (ret > 0) { - mc->log_level = tb & 0xF; - } else { - printk(KERN_ERR "<%s:%s> read fail (err %d)\n", - __func__, mc->name, ret); - } - } - set_fs(old_fs); - - if (mc->use_mif_log && !mc->log_level) - atomic_set(&mc->log_open, 1); - - printk(KERN_ERR "<%s:%s> log level = %d\n", - __func__, mc->name, mc->log_level); -} - -int mif_open_log_file(struct modem_ctl *mc) -{ - struct timeval now; - struct tm date; - mm_segment_t old_fs; - - if (!mc || !mc->use_mif_log) - return -EINVAL; - - if (!mc->log_level) { - printk(KERN_ERR "<%s:%s> IPC logger is disabled.\n", - __func__, mc->name); - return -EINVAL; - } - - if (!mc->fs_ready) { - printk(KERN_ERR "<%s:%s> File system is not ready.\n", - __func__, mc->name); - return -EINVAL; - } - - if (mc->fs_failed) { - printk(KERN_ERR "<%s:%s> Log file cannot be created.\n", - __func__, mc->name); - return -EINVAL; - } - - do_gettimeofday(&now); - time_to_tm((now.tv_sec - sys_tz.tz_minuteswest * 60), 0, &date); - - snprintf(mc->log_path, MAX_MIF_LOG_PATH_LEN, - "%s/%s_mif_log.%ld%02d%02d.%02d%02d%02d.txt", - MIF_LOG_DIR, mc->name, - (1900 + date.tm_year), (1 + date.tm_mon), date.tm_mday, - date.tm_hour, date.tm_min, date.tm_sec); - - old_fs = get_fs(); - set_fs(get_ds()); - mc->log_fp = filp_open(mc->log_path, O_RDWR|O_CREAT, 0666); - set_fs(old_fs); - if (IS_ERR(mc->log_fp)) { - printk(KERN_ERR "<%s:%s> %s open fail\n", - __func__, mc->name, mc->log_path); - mc->log_fp = NULL; - mc->fs_failed = true; - return -ENOENT; - } - - atomic_set(&mc->log_open, 1); - - mif_err("open %s\n", mc->log_path); - - return 0; -} - -void mif_close_log_file(struct modem_ctl *mc) -{ - mm_segment_t old_fs; - - if (!mc || !mc->use_mif_log || !mc->log_level || mc->fs_failed || - !mc->fs_ready || !mc->log_fp) - return; - - atomic_set(&mc->log_open, 0); - - flush_work_sync(&mc->evt_work); - - mif_err("close %s\n", mc->log_path); - - mif_save_logs(mc); - - old_fs = get_fs(); - set_fs(get_ds()); - filp_close(mc->log_fp, NULL); - set_fs(old_fs); - - mc->log_fp = NULL; -} - diff --git a/drivers/misc/modem_if/modem_link_device_dpram.c b/drivers/misc/modem_if/modem_link_device_dpram.c index 862de30..a650ed9 100644 --- a/drivers/misc/modem_if/modem_link_device_dpram.c +++ b/drivers/misc/modem_if/modem_link_device_dpram.c @@ -31,184 +31,79 @@ #include "modem_link_device_dpram.h" #include "modem_utils.h" -const inline char *get_dev_name(int dev) -{ - if (dev == IPC_FMT) - return "FMT"; - else if (dev == IPC_RAW) - return "RAW"; - else if (dev == IPC_RFS) - return "RFS"; - else - return "NONE"; -} - -static void log_dpram_irq(struct dpram_link_device *dpld, u16 int2ap) -{ - struct sk_buff *skb; - struct mif_event_buff *evtb; - struct dpram_irq_buff *irqb; - struct link_device *ld = &dpld->ld; - - skb = alloc_skb(MAX_MIF_EVT_BUFF_SIZE, GFP_ATOMIC); - if (!skb) - return; - - evtb = (struct mif_event_buff *)skb_put(skb, MAX_MIF_EVT_BUFF_SIZE); - memset(evtb, 0, MAX_MIF_EVT_BUFF_SIZE); - - do_gettimeofday(&evtb->tv); - evtb->evt = MIF_IRQ_EVT; - - strncpy(evtb->mc, ld->mc->name, MAX_MIF_NAME_LEN); - strncpy(evtb->ld, ld->name, MAX_MIF_NAME_LEN); - evtb->link_type = ld->link_type; - - irqb = &evtb->dpram_irqb; - - irqb->magic = dpld->dpctl->get_magic(); - irqb->access = dpld->dpctl->get_access(); - - irqb->qsp[IPC_FMT].txq.in = dpld->dpctl->get_tx_head(IPC_FMT); - irqb->qsp[IPC_FMT].txq.out = dpld->dpctl->get_tx_tail(IPC_FMT); - irqb->qsp[IPC_FMT].rxq.in = dpld->dpctl->get_rx_head(IPC_FMT); - irqb->qsp[IPC_FMT].rxq.out = dpld->dpctl->get_rx_tail(IPC_FMT); - - irqb->qsp[IPC_RAW].txq.in = dpld->dpctl->get_tx_head(IPC_RAW); - irqb->qsp[IPC_RAW].txq.out = dpld->dpctl->get_tx_tail(IPC_RAW); - irqb->qsp[IPC_RAW].rxq.in = dpld->dpctl->get_rx_head(IPC_RAW); - irqb->qsp[IPC_RAW].rxq.out = dpld->dpctl->get_rx_tail(IPC_RAW); - - irqb->int2ap = int2ap; - - evtb->rcvd = sizeof(struct dpram_irq_buff); - evtb->len = sizeof(struct dpram_irq_buff); - - mif_irq_log(ld->mc, skb); - mif_flush_logs(ld->mc); -} +/* +** Function prototypes for basic DPRAM operations +*/ +static inline void clear_intr(struct dpram_link_device *dpld); +static inline u16 recv_intr(struct dpram_link_device *dpld); +static inline void send_intr(struct dpram_link_device *dpld, u16 mask); + +static inline u16 get_magic(struct dpram_link_device *dpld); +static inline void set_magic(struct dpram_link_device *dpld, u16 val); +static inline u16 get_access(struct dpram_link_device *dpld); +static inline void set_access(struct dpram_link_device *dpld, u16 val); + +static inline u32 get_tx_head(struct dpram_link_device *dpld, int id); +static inline u32 get_tx_tail(struct dpram_link_device *dpld, int id); +static inline void set_tx_head(struct dpram_link_device *dpld, int id, u32 in); +static inline void set_tx_tail(struct dpram_link_device *dpld, int id, u32 out); +static inline u8 *get_tx_buff(struct dpram_link_device *dpld, int id); +static inline u32 get_tx_buff_size(struct dpram_link_device *dpld, int id); + +static inline u32 get_rx_head(struct dpram_link_device *dpld, int id); +static inline u32 get_rx_tail(struct dpram_link_device *dpld, int id); +static inline void set_rx_head(struct dpram_link_device *dpld, int id, u32 in); +static inline void set_rx_tail(struct dpram_link_device *dpld, int id, u32 out); +static inline u8 *get_rx_buff(struct dpram_link_device *dpld, int id); +static inline u32 get_rx_buff_size(struct dpram_link_device *dpld, int id); + +static inline u16 get_mask_req_ack(struct dpram_link_device *dpld, int id); +static inline u16 get_mask_res_ack(struct dpram_link_device *dpld, int id); +static inline u16 get_mask_send(struct dpram_link_device *dpld, int id); + +static inline bool dpram_circ_valid(u32 size, u32 in, u32 out); + +static void handle_cp_crash(struct dpram_link_device *dpld); +static int trigger_force_cp_crash(struct dpram_link_device *dpld); +static void dpram_dump_memory(struct link_device *ld, char *buff); -static int memcmp16_to_io(const void __iomem *to, void *from, int size) +/* +** Functions for debugging +*/ +static inline void log_dpram_status(struct dpram_link_device *dpld) { - u16 *d = (u16 *)to; - u16 *s = (u16 *)from; - int count = size >> 1; - int diff = 0; - int i; - u16 d1; - u16 s1; - - for (i = 0; i < count; i++) { - d1 = ioread16(d); - s1 = *s; - if (d1 != s1) { - diff++; - mif_info("ERR! [%d] d:0x%04X != s:0x%04X\n", i, d1, s1); - } - d++; - s++; - } - - return diff; + pr_info("mif: %s: {M:0x%X A:%d} {FMT TI:%u TO:%u RI:%u RO:%u} " + "{RAW TI:%u TO:%u RI:%u RO:%u} {INT:0x%X}\n", + dpld->ld.mc->name, + get_magic(dpld), get_access(dpld), + get_tx_head(dpld, IPC_FMT), get_tx_tail(dpld, IPC_FMT), + get_rx_head(dpld, IPC_FMT), get_rx_tail(dpld, IPC_FMT), + get_tx_head(dpld, IPC_RAW), get_tx_tail(dpld, IPC_RAW), + get_rx_head(dpld, IPC_RAW), get_rx_tail(dpld, IPC_RAW), + recv_intr(dpld)); } -static int test_dpram(char *dp_name, u8 __iomem *start, u32 size) +static void set_dpram_map(struct dpram_link_device *dpld, + struct mif_irq_map *map) { - u8 __iomem *dst; - int i; - u16 val; - - mif_info("%s: start = 0x%p, size = %d\n", dp_name, start, size); - - dst = start; - for (i = 0; i < (size >> 1); i++) { - iowrite16((i & 0xFFFF), dst); - dst += 2; - } - - dst = start; - for (i = 0; i < (size >> 1); i++) { - val = ioread16(dst); - if (val != (i & 0xFFFF)) { - mif_info("%s: ERR! dst[%d] 0x%04X != 0x%04X\n", - dp_name, i, val, (i & 0xFFFF)); - return -EINVAL; - } - dst += 2; - } - - dst = start; - for (i = 0; i < (size >> 1); i++) { - iowrite16(0x00FF, dst); - dst += 2; - } - - dst = start; - for (i = 0; i < (size >> 1); i++) { - val = ioread16(dst); - if (val != 0x00FF) { - mif_info("%s: ERR! dst[%d] 0x%04X != 0x00FF\n", - dp_name, i, val); - return -EINVAL; - } - dst += 2; - } - - dst = start; - for (i = 0; i < (size >> 1); i++) { - iowrite16(0x0FF0, dst); - dst += 2; - } - - dst = start; - for (i = 0; i < (size >> 1); i++) { - val = ioread16(dst); - if (val != 0x0FF0) { - mif_info("%s: ERR! dst[%d] 0x%04X != 0x0FF0\n", - dp_name, i, val); - return -EINVAL; - } - dst += 2; - } - - dst = start; - for (i = 0; i < (size >> 1); i++) { - iowrite16(0xFF00, dst); - dst += 2; - } - - dst = start; - for (i = 0; i < (size >> 1); i++) { - val = ioread16(dst); - if (val != 0xFF00) { - mif_info("%s: ERR! dst[%d] 0x%04X != 0xFF00\n", - dp_name, i, val); - return -EINVAL; - } - dst += 2; - } - - dst = start; - for (i = 0; i < (size >> 1); i++) { - iowrite16(0, dst); - dst += 2; - } - - dst = start; - for (i = 0; i < (size >> 1); i++) { - val = ioread16(dst); - if (val != 0) { - mif_info("%s: ERR! dst[%d] 0x%04X != 0\n", - dp_name, i, val); - return -EINVAL; - } - dst += 2; - } - - mif_info("%s: PASS!!!\n", dp_name); - return 0; + map->magic = get_magic(dpld); + map->access = get_access(dpld); + + map->fmt_tx_in = get_tx_head(dpld, IPC_FMT); + map->fmt_tx_out = get_tx_tail(dpld, IPC_FMT); + map->fmt_rx_in = get_rx_head(dpld, IPC_FMT); + map->fmt_rx_out = get_rx_tail(dpld, IPC_FMT); + map->raw_tx_in = get_tx_head(dpld, IPC_RAW); + map->raw_tx_out = get_tx_tail(dpld, IPC_RAW); + map->raw_rx_in = get_rx_head(dpld, IPC_RAW); + map->raw_rx_out = get_rx_tail(dpld, IPC_RAW); + + map->cp2ap = recv_intr(dpld); } +/* +** RXB (DPRAM RX buffer) functions +*/ static struct dpram_rxb *rxbq_create_pool(unsigned size, int count) { struct dpram_rxb *rxb; @@ -304,12 +199,16 @@ static inline void rxb_clear(struct dpram_rxb *rxb) rxb->len = 0; } +/* +** DPRAM operations +*/ static int dpram_register_isr(unsigned irq, irqreturn_t (*isr)(int, void*), - unsigned long flag, const char *name, struct link_device *ld) + unsigned long flag, const char *name, + struct dpram_link_device *dpld) { - int ret = 0; + int ret; - ret = request_irq(irq, isr, flag, name, ld); + ret = request_irq(irq, isr, flag, name, dpld); if (ret) { mif_info("%s: ERR! request_irq fail (err %d)\n", name, ret); return ret; @@ -319,11 +218,311 @@ static int dpram_register_isr(unsigned irq, irqreturn_t (*isr)(int, void*), if (ret) mif_info("%s: ERR! enable_irq_wake fail (err %d)\n", name, ret); - mif_info("%s: IRQ#%d handler registered\n", name, irq); + mif_info("%s (#%d) handler registered\n", name, irq); return 0; } +static inline void clear_intr(struct dpram_link_device *dpld) +{ + if (likely(dpld->dpctl->clear_intr)) + dpld->dpctl->clear_intr(); +} + +static inline u16 recv_intr(struct dpram_link_device *dpld) +{ + if (likely(dpld->dpctl->recv_intr)) + return dpld->dpctl->recv_intr(); + else + return ioread16(dpld->mbx2ap); +} + +static inline void send_intr(struct dpram_link_device *dpld, u16 mask) +{ + if (likely(dpld->dpctl->send_intr)) + dpld->dpctl->send_intr(mask); + else + iowrite16(mask, dpld->mbx2cp); +} + +static inline u16 get_magic(struct dpram_link_device *dpld) +{ + return ioread16(dpld->magic); +} + +static inline void set_magic(struct dpram_link_device *dpld, u16 val) +{ + iowrite16(val, dpld->magic); +} + +static inline u16 get_access(struct dpram_link_device *dpld) +{ + return ioread16(dpld->access); +} + +static inline void set_access(struct dpram_link_device *dpld, u16 val) +{ + iowrite16(val, dpld->access); +} + +static inline u32 get_tx_head(struct dpram_link_device *dpld, int id) +{ + return ioread16(dpld->dev[id]->txq.head); +} + +static inline u32 get_tx_tail(struct dpram_link_device *dpld, int id) +{ + return ioread16(dpld->dev[id]->txq.tail); +} + +static inline void set_tx_head(struct dpram_link_device *dpld, int id, u32 in) +{ + int cnt = 3; + u32 val = 0; + + iowrite16((u16)in, dpld->dev[id]->txq.head); + + do { + /* Check head value written */ + val = ioread16(dpld->dev[id]->txq.head); + if (likely(val == in)) + return; + + mif_err("ERR: %s txq.head(%d) != in(%d)\n", + get_dev_name(id), val, in); + udelay(100); + + /* Write head value again */ + iowrite16((u16)in, dpld->dev[id]->txq.head); + } while (cnt--); + + trigger_force_cp_crash(dpld); +} + +static inline void set_tx_tail(struct dpram_link_device *dpld, int id, u32 out) +{ + int cnt = 3; + u32 val = 0; + + iowrite16((u16)out, dpld->dev[id]->txq.tail); + + do { + /* Check tail value written */ + val = ioread16(dpld->dev[id]->txq.tail); + if (likely(val == out)) + return; + + mif_err("ERR: %s txq.tail(%d) != out(%d)\n", + get_dev_name(id), val, out); + udelay(100); + + /* Write tail value again */ + iowrite16((u16)out, dpld->dev[id]->txq.tail); + } while (cnt--); + + trigger_force_cp_crash(dpld); +} + +static inline u8 *get_tx_buff(struct dpram_link_device *dpld, int id) +{ + return dpld->dev[id]->txq.buff; +} + +static inline u32 get_tx_buff_size(struct dpram_link_device *dpld, int id) +{ + return dpld->dev[id]->txq.size; +} + +static inline u32 get_rx_head(struct dpram_link_device *dpld, int id) +{ + return ioread16(dpld->dev[id]->rxq.head); +} + +static inline u32 get_rx_tail(struct dpram_link_device *dpld, int id) +{ + return ioread16(dpld->dev[id]->rxq.tail); +} + +static inline void set_rx_head(struct dpram_link_device *dpld, int id, u32 in) +{ + int cnt = 3; + u32 val = 0; + + iowrite16((u16)in, dpld->dev[id]->rxq.head); + + do { + /* Check head value written */ + val = ioread16(dpld->dev[id]->rxq.head); + if (val == in) + return; + + mif_err("ERR: %s rxq.head(%d) != in(%d)\n", + get_dev_name(id), val, in); + udelay(100); + + /* Write head value again */ + iowrite16((u16)in, dpld->dev[id]->rxq.head); + } while (cnt--); + + trigger_force_cp_crash(dpld); +} + +static inline void set_rx_tail(struct dpram_link_device *dpld, int id, u32 out) +{ + int cnt = 3; + u32 val = 0; + + iowrite16((u16)out, dpld->dev[id]->rxq.tail); + + do { + /* Check tail value written */ + val = ioread16(dpld->dev[id]->rxq.tail); + if (val == out) + return; + + mif_err("ERR: %s rxq.tail(%d) != out(%d)\n", + get_dev_name(id), val, out); + udelay(100); + + /* Write tail value again */ + iowrite16((u16)out, dpld->dev[id]->rxq.tail); + } while (cnt--); + + trigger_force_cp_crash(dpld); +} + +static inline u8 *get_rx_buff(struct dpram_link_device *dpld, int id) +{ + return dpld->dev[id]->rxq.buff; +} + +static inline u32 get_rx_buff_size(struct dpram_link_device *dpld, int id) +{ + return dpld->dev[id]->rxq.size; +} + +static inline u16 get_mask_req_ack(struct dpram_link_device *dpld, int id) +{ + return dpld->dev[id]->mask_req_ack; +} + +static inline u16 get_mask_res_ack(struct dpram_link_device *dpld, int id) +{ + return dpld->dev[id]->mask_res_ack; +} + +static inline u16 get_mask_send(struct dpram_link_device *dpld, int id) +{ + return dpld->dev[id]->mask_send; +} + +static inline bool dpram_circ_valid(u32 size, u32 in, u32 out) +{ + if (in >= size) + return false; + + if (out >= size) + return false; + + return true; +} + +/* Get free space in the TXQ as well as in & out pointers */ +static inline int dpram_get_txq_space(struct dpram_link_device *dpld, int dev, + u32 qsize, u32 *in, u32 *out) +{ + struct link_device *ld = &dpld->ld; + int cnt = 3; + u32 head; + u32 tail; + int space; + + do { + head = get_tx_head(dpld, dev); + tail = get_tx_tail(dpld, dev); + + space = (head < tail) ? (tail - head - 1) : + (qsize + tail - head - 1); + mif_debug("%s: %s_TXQ qsize[%u] in[%u] out[%u] space[%u]\n", + ld->name, get_dev_name(dev), qsize, head, tail, space); + + if (dpram_circ_valid(qsize, head, tail)) { + *in = head; + *out = tail; + return space; + } + + mif_info("%s: CAUTION! <%pf> " + "%s_TXQ invalid (size:%d in:%d out:%d)\n", + ld->name, __builtin_return_address(0), + get_dev_name(dev), qsize, head, tail); + + udelay(100); + } while (cnt--); + + *in = 0; + *out = 0; + return -EINVAL; +} + +static void dpram_reset_tx_circ(struct dpram_link_device *dpld, int dev) +{ + set_tx_head(dpld, dev, 0); + set_tx_tail(dpld, dev, 0); + if (dev == IPC_FMT) + trigger_force_cp_crash(dpld); +} + +/* Get data size in the RXQ as well as in & out pointers */ +static inline int dpram_get_rxq_rcvd(struct dpram_link_device *dpld, int dev, + u32 qsize, u32 *in, u32 *out) +{ + struct link_device *ld = &dpld->ld; + int cnt = 3; + u32 head; + u32 tail; + u32 rcvd; + + do { + head = get_rx_head(dpld, dev); + tail = get_rx_tail(dpld, dev); + if (head == tail) { + *in = head; + *out = tail; + return 0; + } + + rcvd = (head > tail) ? (head - tail) : (qsize - tail + head); + mif_debug("%s: %s_RXQ qsize[%u] in[%u] out[%u] rcvd[%u]\n", + ld->name, get_dev_name(dev), qsize, head, tail, rcvd); + + if (dpram_circ_valid(qsize, head, tail)) { + *in = head; + *out = tail; + return rcvd; + } + + mif_info("%s: CAUTION! <%pf> " + "%s_RXQ invalid (size:%d in:%d out:%d)\n", + ld->name, __builtin_return_address(0), + get_dev_name(dev), qsize, head, tail); + + udelay(100); + } while (cnt--); + + *in = 0; + *out = 0; + return -EINVAL; +} + +static void dpram_reset_rx_circ(struct dpram_link_device *dpld, int dev) +{ + set_rx_head(dpld, dev, 0); + set_rx_tail(dpld, dev, 0); + if (dev == IPC_FMT) + trigger_force_cp_crash(dpld); +} + /* ** CAUTION : dpram_allow_sleep() MUST be invoked after dpram_wake_up() success */ @@ -335,10 +534,21 @@ static int dpram_wake_up(struct dpram_link_device *dpld) return 0; if (dpld->dpctl->wakeup() < 0) { - mif_info("%s: ERR! <%pF> DPRAM wakeup fail\n", + mif_err("%s: ERR! <%pf> DPRAM wakeup fail once\n", ld->name, __builtin_return_address(0)); - return -EACCES; + + if (dpld->dpctl->sleep) + dpld->dpctl->sleep(); + + udelay(10); + + if (dpld->dpctl->wakeup() < 0) { + mif_err("%s: ERR! <%pf> DPRAM wakeup fail twice\n", + ld->name, __builtin_return_address(0)); + return -EACCES; + } } + atomic_inc(&dpld->accessing); return 0; } @@ -361,19 +571,19 @@ static int dpram_check_access(struct dpram_link_device *dpld) { struct link_device *ld = &dpld->ld; int i; - u16 magic = dpld->dpctl->get_magic(); - u16 access = dpld->dpctl->get_access(); + u16 magic = get_magic(dpld); + u16 access = get_access(dpld); if (likely(magic == DPRAM_MAGIC_CODE && access == 1)) return 0; - for (i = 1; i <= 10; i++) { + for (i = 1; i <= 100; i++) { mif_info("%s: ERR! magic:%X access:%X -> retry:%d\n", ld->name, magic, access, i); - mdelay(1); + udelay(100); - magic = dpld->dpctl->get_magic(); - access = dpld->dpctl->get_access(); + magic = get_magic(dpld); + access = get_access(dpld); if (likely(magic == DPRAM_MAGIC_CODE && access == 1)) return 0; } @@ -388,13 +598,13 @@ static bool dpram_ipc_active(struct dpram_link_device *dpld) /* Check DPRAM mode */ if (ld->mode != LINK_MODE_IPC) { - mif_info("%s: ERR! <%pF> ld->mode != LINK_MODE_IPC\n", + mif_info("%s: <%pf> ld->mode != LINK_MODE_IPC\n", ld->name, __builtin_return_address(0)); return false; } if (dpram_check_access(dpld) < 0) { - mif_info("%s: ERR! <%pF> dpram_check_access fail\n", + mif_info("%s: ERR! <%pf> dpram_check_access fail\n", ld->name, __builtin_return_address(0)); return false; } @@ -402,224 +612,108 @@ static bool dpram_ipc_active(struct dpram_link_device *dpld) return true; } -static inline bool dpram_circ_valid(u32 size, u32 in, u32 out) -{ - if (in >= size) - return false; - - if (out >= size) - return false; - - return true; -} - -/* get the size of the TXQ */ -static inline int dpram_get_txq_size(struct dpram_link_device *dpld, int dev) -{ - return dpld->dpctl->get_tx_buff_size(dev); -} - -/* get in & out pointers of the TXQ */ -static inline void dpram_get_txq_ptrs(struct dpram_link_device *dpld, int dev, - u32 *in, u32 *out) -{ - *in = dpld->dpctl->get_tx_head(dev); - *out = dpld->dpctl->get_tx_tail(dev); -} - -/* get free space in the TXQ as well as in & out pointers */ -static inline int dpram_get_txq_space(struct dpram_link_device *dpld, int dev, - u32 qsize, u32 *in, u32 *out) -{ - struct link_device *ld = &dpld->ld; - - *in = dpld->dpctl->get_tx_head(dev); - *out = dpld->dpctl->get_tx_tail(dev); - - if (!dpram_circ_valid(qsize, *in, *out)) { - mif_info("%s: ERR! <%pF> " - "%s_TXQ invalid (size:%d in:%d out:%d)\n", - ld->name, __builtin_return_address(0), - get_dev_name(dev), qsize, *in, *out); - dpld->dpctl->set_tx_head(dev, 0); - dpld->dpctl->set_tx_tail(dev, 0); - *in = 0; - *out = 0; - return -EINVAL; - } - - return (*in < *out) ? (*out - *in - 1) : (qsize + *out - *in - 1); -} - static void dpram_ipc_write(struct dpram_link_device *dpld, int dev, u32 qsize, u32 in, u32 out, struct sk_buff *skb) { struct link_device *ld = &dpld->ld; - struct modemlink_dpram_control *dpctl = dpld->dpctl; - struct io_device *iod = skbpriv(skb)->iod; - u8 __iomem *dst = dpctl->get_tx_buff(dev); + u8 __iomem *buff = get_tx_buff(dpld, dev); u8 *src = skb->data; u32 len = skb->len; - - /* check queue status */ - mif_debug("%s: {FMT %u %u %u %u} {RAW %u %u %u %u} ...\n", ld->name, - dpctl->get_tx_head(IPC_FMT), dpctl->get_tx_tail(IPC_FMT), - dpctl->get_rx_head(IPC_FMT), dpctl->get_rx_tail(IPC_FMT), - dpctl->get_tx_head(IPC_RAW), dpctl->get_tx_tail(IPC_RAW), - dpctl->get_rx_head(IPC_RAW), dpctl->get_rx_tail(IPC_RAW)); - - if (dev == IPC_FMT) { - mif_ipc_log(ld->mc, MIF_LNK_TX_EVT, iod, ld, src, len); - mif_flush_logs(ld->mc); - } + u32 inp; + struct mif_irq_map map; if (in < out) { /* +++++++++ in ---------- out ++++++++++ */ - memcpy((dst + in), src, len); + memcpy((buff + in), src, len); } else { /* ------ out +++++++++++ in ------------ */ u32 space = qsize - in; /* 1) in -> buffer end */ - memcpy((dst + in), src, ((len > space) ? space : len)); + memcpy((buff + in), src, ((len > space) ? space : len)); /* 2) buffer start -> out */ if (len > space) - memcpy(dst, (src + space), (len - space)); + memcpy(buff, (src + space), (len - space)); } /* update new in pointer */ - in += len; - if (in >= qsize) - in -= qsize; - dpctl->set_tx_head(dev, in); + inp = in + len; + if (inp >= qsize) + inp -= qsize; + set_tx_head(dpld, dev, inp); + + if (dev == IPC_FMT) { + set_dpram_map(dpld, &map); + mif_irq_log(ld->mc->msd, map, "ipc_write", sizeof("ipc_write")); + mif_ipc_log(MIF_IPC_AP2CP, ld->mc->msd, skb->data, skb->len); + } + + if (ld->aligned && memcmp16_to_io((buff + in), src, 4)) { + mif_err("%s: memcmp16_to_io fail\n", ld->name); + trigger_force_cp_crash(dpld); + } } static int dpram_try_ipc_tx(struct dpram_link_device *dpld, int dev) { struct link_device *ld = &dpld->ld; - struct modemlink_dpram_control *dpctl = dpld->dpctl; struct sk_buff_head *txq = ld->skb_txq[dev]; struct sk_buff *skb; - u32 qsize = dpram_get_txq_size(dpld, dev); + unsigned long int flags; + u32 qsize = get_tx_buff_size(dpld, dev); u32 in; u32 out; int space; int copied = 0; - u16 mask = 0; - unsigned long int flags; - while (1) { - skb = skb_dequeue(txq); - if (unlikely(!skb)) - break; + spin_lock_irqsave(&dpld->tx_lock[dev], flags); + while (1) { space = dpram_get_txq_space(dpld, dev, qsize, &in, &out); if (unlikely(space < 0)) { - skb_queue_head(txq, skb); - return -ENOSPC; + spin_unlock_irqrestore(&dpld->tx_lock[dev], flags); + dpram_reset_tx_circ(dpld, dev); + return space; } + skb = skb_dequeue(txq); + if (unlikely(!skb)) + break; + if (unlikely(space < skb->len)) { atomic_set(&dpld->res_required[dev], 1); skb_queue_head(txq, skb); - mask = dpctl->get_mask_req_ack(dev); + spin_unlock_irqrestore(&dpld->tx_lock[dev], flags); mif_info("%s: %s " "qsize[%u] in[%u] out[%u] free[%u] < len[%u]\n", ld->name, get_dev_name(dev), qsize, in, out, space, skb->len); - break; + return -ENOSPC; } - /* TX if there is enough room in the queue - */ - mif_debug("%s: %s " - "qsize[%u] in[%u] out[%u] free[%u] >= len[%u]\n", - ld->name, get_dev_name(dev), - qsize, in, out, space, skb->len); - - spin_lock_irqsave(&dpld->tx_lock, flags); + /* TX if there is enough room in the queue */ dpram_ipc_write(dpld, dev, qsize, in, out, skb); - spin_unlock_irqrestore(&dpld->tx_lock, flags); - copied += skb->len; - dev_kfree_skb_any(skb); } - if (mask) - return -ENOSPC; - else - return copied; -} - -static void dpram_trigger_crash(struct dpram_link_device *dpld) -{ - struct link_device *ld = &dpld->ld; - struct io_device *iod; - int i; - - for (i = 0; i < dpld->max_ipc_dev; i++) { - mif_info("%s: purging %s_skb_txq\b", ld->name, get_dev_name(i)); - skb_queue_purge(ld->skb_txq[i]); - } - - ld->mode = LINK_MODE_ULOAD; - - iod = link_get_iod_with_format(ld, IPC_FMT); - iod->modem_state_changed(iod, STATE_CRASH_EXIT); - - iod = link_get_iod_with_format(ld, IPC_BOOT); - iod->modem_state_changed(iod, STATE_CRASH_EXIT); - - iod = link_get_iod_with_channel(ld, PS_DATA_CH_0); - if (iod) - iodevs_for_each(&iod->mc->commons, iodev_netif_stop, 0); -} - -static int dpram_trigger_force_cp_crash(struct dpram_link_device *dpld) -{ - struct link_device *ld = &dpld->ld; - int ret; - int cnt = 5000; - - mif_info("%s\n", ld->name); - - dpld->dpctl->send_intr(INT_CMD(INT_CMD_CRASH_EXIT)); - - while (cnt--) { - ret = try_wait_for_completion(&dpld->crash_start_complete); - if (ret) - break; - udelay(1000); - } - - if (!ret) { - mif_info("%s: ERR! No CRASH_EXIT ACK from CP\n", ld->name); - dpram_trigger_crash(dpld); - } + spin_unlock_irqrestore(&dpld->tx_lock[dev], flags); - return 0; + return copied; } static void dpram_ipc_rx_task(unsigned long data) { - struct link_device *ld; - struct dpram_link_device *dpld; - struct dpram_rxb *rxb; + struct dpram_link_device *dpld = (struct dpram_link_device *)data; + struct link_device *ld = &dpld->ld; struct io_device *iod; - u32 qlen; + struct dpram_rxb *rxb; + unsigned qlen; int i; - dpld = (struct dpram_link_device *)data; - ld = &dpld->ld; - for (i = 0; i < dpld->max_ipc_dev; i++) { - if (i == IPC_RAW) - iod = link_get_iod_with_format(ld, IPC_MULTI_RAW); - else - iod = link_get_iod_with_format(ld, i); - + iod = dpld->iod[i]; qlen = rxbq_size(&dpld->rxbq[i]); while (qlen > 0) { rxb = rxbq_get_data_rxb(&dpld->rxbq[i]); @@ -656,37 +750,24 @@ static void dpram_ipc_read(struct dpram_link_device *dpld, int dev, u8 *dst, ret == 0 : no data ret > 0 : valid data */ -static int dpram_ipc_recv_data(struct dpram_link_device *dpld, int dev, - u16 non_cmd) +static int dpram_ipc_recv_data_with_rxb(struct dpram_link_device *dpld, int dev) { - struct modemlink_dpram_control *dpctl = dpld->dpctl; struct link_device *ld = &dpld->ld; struct dpram_rxb *rxb; - u8 __iomem *src = dpctl->get_rx_buff(dev); - u32 in = dpctl->get_rx_head(dev); - u32 out = dpctl->get_rx_tail(dev); - u32 qsize = dpctl->get_rx_buff_size(dev); - u32 rcvd = 0; - - if (in == out) - return 0; - - if (dev == IPC_FMT) - log_dpram_irq(dpld, non_cmd); - - /* Get data length in DPRAM*/ - rcvd = (in > out) ? (in - out) : (qsize - out + in); + u8 __iomem *src = get_rx_buff(dpld, dev); + u32 qsize = get_rx_buff_size(dpld, dev); + u32 in; + u32 out; + u32 rcvd; + struct mif_irq_map map; - mif_debug("%s: %s qsize[%u] in[%u] out[%u] rcvd[%u]\n", - ld->name, get_dev_name(dev), qsize, in, out, rcvd); + rcvd = dpram_get_rxq_rcvd(dpld, dev, qsize, &in, &out); + if (rcvd <= 0) + return rcvd; - /* Check each queue */ - if (!dpram_circ_valid(qsize, in, out)) { - mif_info("%s: ERR! %s_RXQ invalid (size:%d in:%d out:%d)\n", - ld->name, get_dev_name(dev), qsize, in, out); - dpctl->set_rx_head(dev, 0); - dpctl->set_rx_tail(dev, 0); - return -EINVAL; + if (dev == IPC_FMT) { + set_dpram_map(dpld, &map); + mif_irq_log(ld->mc->msd, map, "ipc_recv", sizeof("ipc_recv")); } /* Allocate an rxb */ @@ -704,30 +785,95 @@ static int dpram_ipc_recv_data(struct dpram_link_device *dpld, int dev, out += rcvd; if (out >= qsize) out -= qsize; - dpctl->set_rx_tail(dev, out); + set_rx_tail(dpld, dev, out); return rcvd; } -static void dpram_purge_rx_circ(struct dpram_link_device *dpld, int dev) +/* + ret < 0 : error + ret == 0 : no data + ret > 0 : valid data +*/ +static int dpram_ipc_recv_data_with_skb(struct dpram_link_device *dpld, int dev) { - u32 in = dpld->dpctl->get_rx_head(dev); - dpld->dpctl->set_rx_tail(dev, in); + struct link_device *ld = &dpld->ld; + struct io_device *iod = dpld->iod[dev]; + struct sk_buff *skb; + u8 __iomem *src = get_rx_buff(dpld, dev); + u32 qsize = get_rx_buff_size(dpld, dev); + u32 in; + u32 out; + u32 rcvd; + int rest; + u8 *frm; + u8 *dst; + unsigned int len; + unsigned int pad; + unsigned int tot; + struct mif_irq_map map; + + rcvd = dpram_get_rxq_rcvd(dpld, dev, qsize, &in, &out); + if (rcvd <= 0) + return rcvd; + + if (dev == IPC_FMT) { + set_dpram_map(dpld, &map); + mif_irq_log(ld->mc->msd, map, "ipc_recv", sizeof("ipc_recv")); + } + + rest = rcvd; + while (rest > 0) { + frm = src + out; + if (unlikely(!sipc5_start_valid(frm[0]))) { + mif_err("%s: ERR! %s invalid start 0x%02X\n", + ld->name, get_dev_name(dev), frm[0]); + skb_queue_purge(&dpld->skb_rxq[dev]); + return -EBADMSG; + } + + len = sipc5_get_frame_sz16(frm); + if (unlikely(len > rest)) { + mif_err("%s: ERR! %s len %d > rest %d\n", + ld->name, get_dev_name(dev), len, rest); + skb_queue_purge(&dpld->skb_rxq[dev]); + return -EBADMSG; + } + + pad = sipc5_calc_padding_size(len); + tot = len + pad; + + /* Allocate an skb */ + skb = dev_alloc_skb(tot); + if (!skb) { + mif_err("%s: ERR! %s dev_alloc_skb fail\n", + ld->name, get_dev_name(dev)); + return -ENOMEM; + } + + /* Read data from each DPRAM buffer */ + dst = skb_put(skb, tot); + dpram_ipc_read(dpld, dev, dst, src, out, tot, qsize); + skb_trim(skb, len); + iod->recv_skb(iod, ld, skb); + + /* Calculate and set new out */ + rest -= tot; + out += tot; + if (out >= qsize) + out -= qsize; + } + + set_rx_tail(dpld, dev, out); + + return rcvd; } -static void non_command_handler(struct dpram_link_device *dpld, u16 non_cmd) +static void non_command_handler(struct dpram_link_device *dpld, u16 intr) { - struct modemlink_dpram_control *dpctl = dpld->dpctl; struct link_device *ld = &dpld->ld; - struct sk_buff_head *txq; - struct sk_buff *skb; - int i; + int i = 0; int ret = 0; - int copied = 0; - u32 in; - u32 out; - u16 mask = 0; - u16 req_mask = 0; u16 tx_mask = 0; if (!dpram_ipc_active(dpld)) @@ -735,85 +881,154 @@ static void non_command_handler(struct dpram_link_device *dpld, u16 non_cmd) /* Read data from DPRAM */ for (i = 0; i < dpld->max_ipc_dev; i++) { - ret = dpram_ipc_recv_data(dpld, i, non_cmd); + if (dpld->use_skb) + ret = dpram_ipc_recv_data_with_skb(dpld, i); + else + ret = dpram_ipc_recv_data_with_rxb(dpld, i); if (ret < 0) - dpram_purge_rx_circ(dpld, i); + dpram_reset_rx_circ(dpld, i); /* Check and process REQ_ACK (at this time, in == out) */ - if (non_cmd & dpctl->get_mask_req_ack(i)) { + if (intr & get_mask_req_ack(dpld, i)) { mif_debug("%s: send %s_RES_ACK\n", ld->name, get_dev_name(i)); - mask = dpctl->get_mask_res_ack(i); - dpctl->send_intr(INT_NON_CMD(mask)); + tx_mask |= get_mask_res_ack(dpld, i); } } - /* Schedule soft IRQ for RX */ - tasklet_hi_schedule(&dpld->rx_tsk); + if (!dpld->use_skb) { + /* Schedule soft IRQ for RX */ + tasklet_hi_schedule(&dpld->rx_tsk); + } /* Try TX via DPRAM */ for (i = 0; i < dpld->max_ipc_dev; i++) { if (atomic_read(&dpld->res_required[i]) > 0) { - dpram_get_txq_ptrs(dpld, i, &in, &out); - if (likely(in == out)) { - ret = dpram_try_ipc_tx(dpld, i); - if (ret > 0) { - atomic_set(&dpld->res_required[i], 0); - tx_mask |= dpctl->get_mask_send(i); - } else { - req_mask |= dpctl->get_mask_req_ack(i); - } - } else { - req_mask |= dpctl->get_mask_req_ack(i); + ret = dpram_try_ipc_tx(dpld, i); + if (ret > 0) { + atomic_set(&dpld->res_required[i], 0); + tx_mask |= get_mask_send(dpld, i); + } else if (ret == -ENOSPC) { + tx_mask |= get_mask_req_ack(dpld, i); } } } - if (req_mask || tx_mask) { - tx_mask |= req_mask; - dpctl->send_intr(INT_NON_CMD(tx_mask)); + if (tx_mask) { + send_intr(dpld, INT_NON_CMD(tx_mask)); mif_debug("%s: send intr 0x%04X\n", ld->name, tx_mask); } } +static void handle_cp_crash(struct dpram_link_device *dpld) +{ + struct link_device *ld = &dpld->ld; + struct io_device *iod; + int i; + + for (i = 0; i < dpld->max_ipc_dev; i++) { + mif_info("%s: purging %s_skb_txq\b", ld->name, get_dev_name(i)); + skb_queue_purge(ld->skb_txq[i]); + } + + iod = link_get_iod_with_format(ld, IPC_FMT); + iod->modem_state_changed(iod, STATE_CRASH_EXIT); + + iod = link_get_iod_with_format(ld, IPC_BOOT); + iod->modem_state_changed(iod, STATE_CRASH_EXIT); + + iod = link_get_iod_with_channel(ld, PS_DATA_CH_0); + if (iod) + iodevs_for_each(iod->msd, iodev_netif_stop, 0); +} + +static void handle_no_crash_ack(unsigned long arg) +{ + struct dpram_link_device *dpld = (struct dpram_link_device *)arg; + struct link_device *ld = &dpld->ld; + + mif_err("%s: ERR! No CRASH_EXIT ACK from CP\n", ld->mc->name); + + if (!wake_lock_active(&dpld->wlock)) + wake_lock(&dpld->wlock); + + handle_cp_crash(dpld); +} + +static int trigger_force_cp_crash(struct dpram_link_device *dpld) +{ + struct link_device *ld = &dpld->ld; + + if (ld->mode == LINK_MODE_ULOAD) { + mif_err("%s: CP crash is already in progress\n", ld->mc->name); + return 0; + } + + ld->mode = LINK_MODE_ULOAD; + mif_err("%s: called by %pf\n", ld->name, __builtin_return_address(0)); + + if (dpld->dp_type == CP_IDPRAM) + dpram_wake_up(dpld); + + send_intr(dpld, INT_CMD(INT_CMD_CRASH_EXIT)); + + mif_add_timer(&dpld->crash_ack_timer, FORCE_CRASH_ACK_TIMEOUT, + handle_no_crash_ack, (unsigned long)dpld); + + return 0; +} + static int dpram_init_ipc(struct dpram_link_device *dpld) { struct link_device *ld = &dpld->ld; - struct modemlink_dpram_control *dpctl = dpld->dpctl; int i; if (ld->mode == LINK_MODE_IPC && - dpctl->get_magic() == DPRAM_MAGIC_CODE && - dpctl->get_access() == 1) + get_magic(dpld) == DPRAM_MAGIC_CODE && + get_access(dpld) == 1) mif_info("%s: IPC already initialized\n", ld->name); /* Clear pointers in every circular queue */ for (i = 0; i < dpld->max_ipc_dev; i++) { - dpctl->set_tx_head(i, 0); - dpctl->set_tx_tail(i, 0); - dpctl->set_rx_head(i, 0); - dpctl->set_rx_tail(i, 0); + set_tx_head(dpld, i, 0); + set_tx_tail(dpld, i, 0); + set_rx_head(dpld, i, 0); + set_rx_tail(dpld, i, 0); } - /* Enable IPC */ - dpctl->set_magic(DPRAM_MAGIC_CODE); - dpctl->set_access(1); - if (dpctl->get_magic() != DPRAM_MAGIC_CODE || dpctl->get_access() != 1) - return -EACCES; + /* Initialize variables for efficient TX/RX processing */ + for (i = 0; i < dpld->max_ipc_dev; i++) + dpld->iod[i] = link_get_iod_with_format(ld, i); + dpld->iod[IPC_RAW] = link_get_iod_with_format(ld, IPC_MULTI_RAW); - ld->mode = LINK_MODE_IPC; + if (dpld->iod[IPC_RAW]->recv_skb) + dpld->use_skb = true; - for (i = 0; i < dpld->max_ipc_dev; i++) + for (i = 0; i < dpld->max_ipc_dev; i++) { + spin_lock_init(&dpld->tx_lock[i]); atomic_set(&dpld->res_required[i], 0); + skb_queue_purge(&dpld->skb_rxq[i]); + } + /* Enable IPC */ atomic_set(&dpld->accessing, 0); + set_magic(dpld, DPRAM_MAGIC_CODE); + set_access(dpld, 1); + if (get_magic(dpld) != DPRAM_MAGIC_CODE || get_access(dpld) != 1) + return -EACCES; + + ld->mode = LINK_MODE_IPC; + + if (wake_lock_active(&dpld->wlock)) + wake_unlock(&dpld->wlock); + return 0; } static void cmd_req_active_handler(struct dpram_link_device *dpld) { - dpld->dpctl->send_intr(INT_CMD(INT_CMD_RES_ACTIVE)); + send_intr(dpld, INT_CMD(INT_CMD_RES_ACTIVE)); } static void cmd_crash_reset_handler(struct dpram_link_device *dpld) @@ -821,10 +1036,13 @@ static void cmd_crash_reset_handler(struct dpram_link_device *dpld) struct link_device *ld = &dpld->ld; struct io_device *iod = NULL; - mif_info("%s: Recv 0xC7 (CRASH_RESET)\n", ld->name); - ld->mode = LINK_MODE_ULOAD; + if (!wake_lock_active(&dpld->wlock)) + wake_lock(&dpld->wlock); + + mif_err("%s: Recv 0xC7 (CRASH_RESET)\n", ld->name); + iod = link_get_iod_with_format(ld, IPC_FMT); iod->modem_state_changed(iod, STATE_CRASH_RESET); @@ -835,19 +1053,36 @@ static void cmd_crash_reset_handler(struct dpram_link_device *dpld) static void cmd_crash_exit_handler(struct dpram_link_device *dpld) { struct link_device *ld = &dpld->ld; - - mif_info("%s: Recv 0xC9 (CRASH_EXIT)\n", ld->name); + u32 size = dpld->dpctl->dp_size; + char *dpram_buff = NULL; ld->mode = LINK_MODE_ULOAD; - complete_all(&dpld->crash_start_complete); + if (!wake_lock_active(&dpld->wlock)) + wake_lock(&dpld->wlock); - if (ld->mdm_data->modem_type == QC_MDM6600) { - if (dpld->dpctl->log_disp) - dpld->dpctl->log_disp(dpld->dpctl); + mif_err("%s: Recv 0xC9 (CRASH_EXIT)\n", ld->name); + + if (dpld->dp_type == CP_IDPRAM) + dpram_wake_up(dpld); + + dpram_buff = kzalloc(size + (MAX_MIF_SEPA_SIZE * 2), GFP_ATOMIC); + if (!dpram_buff) { + mif_err("DPRAM dump failed!!\n"); + } else { + memset(dpram_buff, 0, size + (MAX_MIF_SEPA_SIZE * 2)); + memcpy(dpram_buff, MIF_SEPARATOR_DPRAM, MAX_MIF_SEPA_SIZE); + memcpy(dpram_buff + MAX_MIF_SEPA_SIZE, &size, sizeof(u32)); + dpram_buff += (MAX_MIF_SEPA_SIZE * 2); + dpram_dump_memory(ld, dpram_buff); } - dpram_trigger_crash(dpld); + del_timer(&dpld->crash_ack_timer); + + if (dpld->ext_op && dpld->ext_op->crash_log) + dpld->ext_op->crash_log(dpld); + + handle_cp_crash(dpld); } static void cmd_phone_start_handler(struct dpram_link_device *dpld) @@ -865,19 +1100,17 @@ static void cmd_phone_start_handler(struct dpram_link_device *dpld) return; } - if (ld->mdm_data->modem_type == SEC_CMC221) { - if (ld->mc->phone_state != STATE_ONLINE) { - mif_info("%s: phone_state: %d -> ONLINE\n", - ld->name, ld->mc->phone_state); - iod->modem_state_changed(iod, STATE_ONLINE); - } - } else if (ld->mdm_data->modem_type == QC_MDM6600) { - if (dpld->dpctl->phone_boot_start_handler) - dpld->dpctl->phone_boot_start_handler(dpld->dpctl); + if (dpld->ext_op && dpld->ext_op->cp_start_handler) + dpld->ext_op->cp_start_handler(dpld); + + if (ld->mc->phone_state != STATE_ONLINE) { + mif_info("%s: phone_state: %d -> ONLINE\n", + ld->name, ld->mc->phone_state); + iod->modem_state_changed(iod, STATE_ONLINE); } mif_info("%s: Send 0xC2 (INIT_END)\n", ld->name); - dpld->dpctl->send_intr(INT_CMD(INT_CMD_INIT_END)); + send_intr(dpld, INT_CMD(INT_CMD_INIT_END)); } static void command_handler(struct dpram_link_device *dpld, u16 cmd) @@ -942,7 +1175,7 @@ static void ext_command_handler(struct dpram_link_device *dpld, u16 cmd) if (dpld->dpctl->setup_speed) { dpld->dpctl->setup_speed(DPRAM_SPEED_LOW); resp = INT_EXT_CMD(EXT_CMD_SET_SPEED_LOW); - dpld->dpctl->send_intr(resp); + send_intr(dpld, resp); } break; @@ -950,7 +1183,7 @@ static void ext_command_handler(struct dpram_link_device *dpld, u16 cmd) if (dpld->dpctl->setup_speed) { dpld->dpctl->setup_speed(DPRAM_SPEED_MID); resp = INT_EXT_CMD(EXT_CMD_SET_SPEED_MID); - dpld->dpctl->send_intr(resp); + send_intr(dpld, resp); } break; @@ -958,7 +1191,7 @@ static void ext_command_handler(struct dpram_link_device *dpld, u16 cmd) if (dpld->dpctl->setup_speed) { dpld->dpctl->setup_speed(DPRAM_SPEED_HIGH); resp = INT_EXT_CMD(EXT_CMD_SET_SPEED_HIGH); - dpld->dpctl->send_intr(resp); + send_intr(dpld, resp); } break; @@ -968,494 +1201,141 @@ static void ext_command_handler(struct dpram_link_device *dpld, u16 cmd) } } -static void cmc22x_idpram_enable_ipc(struct dpram_link_device *dpld) -{ - dpram_init_ipc(dpld); -} - -static int cmc22x_idpram_wait_response(struct dpram_link_device *dpld, u32 resp) +static void udl_command_handler(struct dpram_link_device *dpld, u16 cmd) { struct link_device *ld = &dpld->ld; - int count = 50000; - u32 rcvd = 0; - - if (resp == CMC22x_CP_REQ_NV_DATA) { - while (1) { - rcvd = ioread32(dpld->bt_map.resp); - if (rcvd == resp) - break; - - rcvd = dpld->dpctl->recv_msg(); - if (rcvd == 0x9999) { - mif_info("%s: Invalid resp 0x%04X\n", - ld->name, rcvd); - panic("CP Crash ... BAD CRC in CP"); - } - - if (count-- < 0) { - mif_info("%s: Invalid resp 0x%08X\n", - ld->name, rcvd); - return -EAGAIN; - } - - udelay(100); - } - } else { - while (1) { - rcvd = dpld->dpctl->recv_msg(); - - if (rcvd == resp) - break; - - if (resp == CMC22x_CP_RECV_NV_END && - rcvd == CMC22x_CP_CAL_BAD) { - mif_info("%s: CMC22x_CP_CAL_BAD\n", ld->name); - break; - } - - if (count-- < 0) { - mif_info("%s: Invalid resp 0x%04X\n", - ld->name, rcvd); - return -EAGAIN; - } - - udelay(100); - } - } - - return rcvd; -} - -static int cmc22x_idpram_send_boot(struct link_device *ld, unsigned long arg) -{ - int err = 0; - int cnt = 0; - struct dpram_link_device *dpld = to_dpram_link_device(ld); - u8 __iomem *bt_buff = dpld->bt_map.buff; - struct dpram_boot_img cp_img; - u8 *img_buff = NULL; - - ld->mode = LINK_MODE_BOOT; - - dpld->dpctl->setup_speed(DPRAM_SPEED_LOW); - - /* Test memory... After testing, memory is cleared. */ - if (test_dpram(ld->name, bt_buff, dpld->bt_map.size) < 0) { - mif_info("%s: ERR! test_dpram fail!\n", ld->name); - ld->mode = LINK_MODE_INVALID; - return -EIO; - } - - /* Get information about the boot image */ - err = copy_from_user((struct dpram_boot_img *)&cp_img, (void *)arg, - sizeof(struct dpram_boot_img)); - mif_info("%s: CP image addr = 0x%08X, size = %d\n", - ld->name, (int)cp_img.addr, cp_img.size); - - /* Alloc a buffer for the boot image */ - img_buff = kzalloc(dpld->bt_map.size, GFP_KERNEL); - if (!img_buff) { - mif_info("%s: ERR! kzalloc fail\n", ld->name); - ld->mode = LINK_MODE_INVALID; - return -ENOMEM; - } - /* Copy boot image from the user space to the image buffer */ - err = copy_from_user(img_buff, cp_img.addr, cp_img.size); - - /* Copy boot image to DPRAM and verify it */ - memcpy(bt_buff, img_buff, cp_img.size); - if (memcmp16_to_io(bt_buff, img_buff, cp_img.size)) { - mif_info("%s: ERR! Boot may be broken!!!\n", ld->name); - goto err; + if (cmd & UDL_RESULT_FAIL) { + mif_info("%s: ERR! Command failed: %04x\n", ld->name, cmd); + return; } - dpld->dpctl->reset(); - udelay(1000); - - if (cp_img.mode == CMC22x_BOOT_MODE_NORMAL) { - mif_info("%s: CMC22x_BOOT_MODE_NORMAL\n", ld->name); - mif_info("%s: Send req 0x%08X\n", ld->name, cp_img.req); - iowrite32(cp_img.req, dpld->bt_map.req); - - /* Wait for cp_img.resp for 1 second */ - mif_info("%s: Wait resp 0x%08X\n", ld->name, cp_img.resp); - while (ioread32(dpld->bt_map.resp) != cp_img.resp) { - cnt++; - msleep_interruptible(10); - if (cnt > 100) { - mif_info("%s: ERR! Invalid resp 0x%08X\n", - ld->name, ioread32(dpld->bt_map.resp)); - goto err; - } - } - } else { - mif_info("%s: CMC22x_BOOT_MODE_DUMP\n", ld->name); + switch (UDL_CMD_MASK(cmd)) { + case UDL_CMD_RECV_READY: + mif_debug("%s: Send CP-->AP RECEIVE_READY\n", ld->name); + send_intr(dpld, CMD_IMG_START_REQ); + break; + default: + complete_all(&dpld->udl_cmd_complete); } - - kfree(img_buff); - - mif_info("%s: Send BOOT done\n", ld->name); - - if (dpld->dpctl->setup_speed) - dpld->dpctl->setup_speed(DPRAM_SPEED_HIGH); - - return 0; - -err: - ld->mode = LINK_MODE_INVALID; - kfree(img_buff); - - mif_info("%s: ERR! Boot send fail!!!\n", ld->name); - return -EIO; } -static int cmc22x_idpram_send_main(struct link_device *ld, struct sk_buff *skb) +static inline void dpram_ipc_rx(struct dpram_link_device *dpld, u16 intr) { - int err = 0; - int ret = 0; - struct dpram_link_device *dpld = to_dpram_link_device(ld); - struct dpram_boot_frame *bf = (struct dpram_boot_frame *)skb->data; - u8 __iomem *buff = (dpld->bt_map.buff + bf->offset); - - if ((bf->offset + bf->len) > dpld->bt_map.size) { - mif_info("%s: ERR! Out of DPRAM boundary\n", ld->name); - err = -EINVAL; - goto exit; - } - - if (bf->len) - memcpy(buff, bf->data, bf->len); - - if (bf->request) - dpld->dpctl->send_msg((u16)bf->request); - - if (bf->response) { - err = cmc22x_idpram_wait_response(dpld, bf->response); - if (err < 0) - mif_info("%s: ERR! wait_response fail (err %d)\n", - ld->name, err); - } - - if (bf->request == CMC22x_CAL_NV_DOWN_END) { - mif_info("%s: CMC22x_CAL_NV_DOWN_END\n", ld->name); - cmc22x_idpram_enable_ipc(dpld); - } - -exit: - if (err < 0) - ret = err; + if (unlikely(INT_CMD_VALID(intr))) + command_handler(dpld, intr); else - ret = skb->len; - - dev_kfree_skb_any(skb); - - return ret; + non_command_handler(dpld, intr); } -static void cmc22x_idpram_wait_dump(unsigned long arg) +static inline void dpram_intr_handler(struct dpram_link_device *dpld, u16 intr) { - struct dpram_link_device *dpld = (struct dpram_link_device *)arg; - u16 msg; - - if (!dpld) { - mif_info("ERR! dpld == NULL\n"); - return; - } + char *name = dpld->ld.name; - msg = dpld->dpctl->recv_msg(); - - if (msg == CMC22x_CP_DUMP_END) { - complete_all(&dpld->dump_recv_done); - return; - } - - if (((dpld->dump_rcvd & 0x1) == 0) && (msg == CMC22x_1ST_BUFF_FULL)) { - complete_all(&dpld->dump_recv_done); + if (unlikely(intr == INT_POWERSAFE_FAIL)) { + mif_info("%s: intr == INT_POWERSAFE_FAIL\n", name); return; } - if (((dpld->dump_rcvd & 0x1) == 1) && (msg == CMC22x_2ND_BUFF_FULL)) { - complete_all(&dpld->dump_recv_done); + if (unlikely(EXT_UDL_CMD(intr))) { + if (likely(EXT_INT_VALID(intr))) { + if (UDL_CMD_VALID(intr)) + udl_command_handler(dpld, intr); + else if (EXT_CMD_VALID(intr)) + ext_command_handler(dpld, intr); + else + mif_info("%s: ERR! invalid intr 0x%04X\n", + name, intr); + } else { + mif_info("%s: ERR! invalid intr 0x%04X\n", name, intr); + } return; } - mif_add_timer(&dpld->dump_timer, CMC22x_DUMP_WAIT_TIMEOVER, - cmc22x_idpram_wait_dump, (unsigned long)dpld); -} - -static int cmc22x_idpram_upload(struct dpram_link_device *dpld, - struct dpram_dump_arg *dumparg) -{ - struct link_device *ld = &dpld->ld; - int ret; - u8 __iomem *src; - int buff_size = CMC22x_DUMP_BUFF_SIZE; - - if ((dpld->dump_rcvd & 0x1) == 0) - dpld->dpctl->send_msg(CMC22x_1ST_BUFF_READY); - else - dpld->dpctl->send_msg(CMC22x_2ND_BUFF_READY); - - init_completion(&dpld->dump_recv_done); - - mif_add_timer(&dpld->dump_timer, CMC22x_DUMP_WAIT_TIMEOVER, - cmc22x_idpram_wait_dump, (unsigned long)dpld); - - ret = wait_for_completion_interruptible_timeout( - &dpld->dump_recv_done, DUMP_TIMEOUT); - if (!ret) { - mif_info("%s: ERR! CP didn't send dump data!!!\n", ld->name); - goto err_out; - } - - if (dpld->dpctl->recv_msg() == CMC22x_CP_DUMP_END) { - mif_info("%s: CMC22x_CP_DUMP_END\n", ld->name); - wake_unlock(&dpld->dpram_wake_lock); - return 0; - } - - if ((dpld->dump_rcvd & 0x1) == 0) - src = dpld->ul_map.buff; + if (likely(INT_VALID(intr))) + dpram_ipc_rx(dpld, intr); else - src = dpld->ul_map.buff + CMC22x_DUMP_BUFF_SIZE; - - memcpy(dpld->buff, src, buff_size); - - ret = copy_to_user(dumparg->buff, dpld->buff, buff_size); - if (ret < 0) { - mif_info("%s: ERR! copy_to_user fail\n", ld->name); - goto err_out; - } - - dpld->dump_rcvd++; - return buff_size; - -err_out: - wake_unlock(&dpld->dpram_wake_lock); - return -EIO; + mif_info("%s: ERR! invalid intr 0x%04X\n", name, intr); } -static int cbp72_edpram_wait_response(struct dpram_link_device *dpld, u32 resp) +static irqreturn_t ap_idpram_irq_handler(int irq, void *data) { - struct link_device *ld = &dpld->ld; - int ret; - int int2cp; + struct dpram_link_device *dpld = (struct dpram_link_device *)data; + struct link_device *ld = (struct link_device *)&dpld->ld; + u16 int2ap = recv_intr(dpld); - ret = wait_for_completion_interruptible_timeout( - &dpld->udl_cmd_complete, UDL_TIMEOUT); - if (!ret) { - mif_info("%s: ERR! No UDL_CMD_RESP!!!\n", ld->name); - return -ENXIO; - } - - int2cp = dpld->dpctl->recv_intr(); - mif_debug("%s: int2cp = 0x%x\n", ld->name, int2cp); - if (resp == int2cp || int2cp == 0xA700) - return int2cp; - else - return -EINVAL; -} - -static int cbp72_edpram_send_bin(struct link_device *ld, struct sk_buff *skb) -{ - struct dpram_link_device *dpld = to_dpram_link_device(ld); - struct dpram_boot_frame *bf = (struct dpram_boot_frame *)skb->data; - u8 __iomem *buff = dpld->bt_map.buff; - int err = 0; - - if (bf->len > dpld->bt_map.size) { - mif_info("%s: ERR! Out of DPRAM boundary\n", ld->name); - err = -EINVAL; - goto exit; - } - - if (bf->len) - memcpy(buff, bf->data, bf->len); - - init_completion(&dpld->udl_cmd_complete); + if (unlikely(ld->mode == LINK_MODE_OFFLINE)) + return IRQ_HANDLED; - if (bf->request) - dpld->dpctl->send_intr((u16)bf->request); - - if (bf->response) { - err = cbp72_edpram_wait_response(dpld, bf->response); - if (err < 0) { - mif_info("%s: ERR! wait_response fail (%d)\n", - ld->name, err); - goto exit; - } else if (err == bf->response) { - err = skb->len; - } - } + dpram_intr_handler(dpld, int2ap); -exit: - dev_kfree_skb_any(skb); - return err; + return IRQ_HANDLED; } -static int dpram_upload(struct dpram_link_device *dpld, - struct dpram_dump_arg *dump, unsigned char __user *target) +static irqreturn_t cp_idpram_irq_handler(int irq, void *data) { - struct link_device *ld = &dpld->ld; - struct ul_header header; - u8 *dest; - u8 *buff = vmalloc(DP_DEFAULT_DUMP_LEN); - u16 plen = 0; - int err = 0; - int ret = 0; - int buff_size = 0; - - mif_info("\n"); + struct dpram_link_device *dpld = (struct dpram_link_device *)data; + struct link_device *ld = (struct link_device *)&dpld->ld; + u16 int2ap; - wake_lock(&dpld->dpram_wake_lock); - init_completion(&dpld->udl_cmd_complete); - - mif_info("%s: req = %x, resp =%x", ld->name, dump->req, dump->resp); - - if (dump->req) - dpld->dpctl->send_intr((u16)dump->req); - - if (dump->resp) { - err = cbp72_edpram_wait_response(dpld, dump->resp); - if (err < 0) { - mif_info("%s: ERR! wait_response fail (%d)\n", - ld->name, err); - goto exit; - } - } - - if (dump->cmd) - return err; - - dest = (u8 *)dpld->ul_map.buff; - - header.bop = *(u8 *)(dest); - header.total_frame = *(u16 *)(dest + 1); - header.curr_frame = *(u16 *)(dest + 3); - header.len = *(u16 *)(dest + 5); - - mif_info("%s: total frame:%d, current frame:%d, data len:%d\n", - ld->name, header.total_frame, header.curr_frame, header.len); - - plen = min_t(u16, header.len, DP_DEFAULT_DUMP_LEN); - - memcpy(buff, dest + sizeof(struct ul_header), plen); - ret = copy_to_user(dump->buff, buff, plen); - if (ret < 0) { - mif_info("%s: copy_to_user fail\n", ld->name); - goto exit; - } - buff_size = plen; + if (unlikely(ld->mode == LINK_MODE_OFFLINE)) + return IRQ_HANDLED; - ret = copy_to_user(target + 4, &buff_size, sizeof(int)); - if (ret < 0) { - mif_info("%s: copy_to_user fail\n", ld->name); - goto exit; + if (dpram_wake_up(dpld) < 0) { + log_dpram_status(dpld); + trigger_force_cp_crash(dpld); + return IRQ_HANDLED; } - wake_unlock(&dpld->dpram_wake_lock); + int2ap = recv_intr(dpld); - return err; + dpram_intr_handler(dpld, int2ap); -exit: - vfree(buff); - iowrite32(0, dpld->ul_map.magic); - wake_unlock(&dpld->dpram_wake_lock); - return -EIO; -} + clear_intr(dpld); -static void udl_cmd_handler(struct dpram_link_device *dpld, u16 cmd) -{ - struct link_device *ld = &dpld->ld; - - if (cmd & UDL_RESULT_FAIL) { - mif_info("%s: ERR! Command failed: %04x\n", ld->name, cmd); - return; - } + dpram_allow_sleep(dpld); - switch (UDL_CMD_MASK(cmd)) { - case UDL_CMD_RECEIVE_READY: - mif_debug("%s: Send CP-->AP RECEIVE_READY\n", ld->name); - dpld->dpctl->send_intr(CMD_IMG_START_REQ); - break; - default: - complete_all(&dpld->udl_cmd_complete); - } + return IRQ_HANDLED; } -static irqreturn_t dpram_irq_handler(int irq, void *data) +static irqreturn_t ext_dpram_irq_handler(int irq, void *data) { - struct link_device *ld = (struct link_device *)data; - struct dpram_link_device *dpld = to_dpram_link_device(ld); - u16 int2ap = 0; + struct dpram_link_device *dpld = (struct dpram_link_device *)data; + struct link_device *ld = (struct link_device *)&dpld->ld; + u16 int2ap = recv_intr(dpld); - if (!ld->mc || ld->mc->phone_state == STATE_OFFLINE) + if (unlikely(ld->mode == LINK_MODE_OFFLINE)) return IRQ_HANDLED; - if (dpram_wake_up(dpld) < 0) - return IRQ_HANDLED; - - int2ap = dpld->dpctl->recv_intr(); - - if (dpld->dpctl->clear_intr) - dpld->dpctl->clear_intr(); - - if (int2ap == INT_POWERSAFE_FAIL) { - mif_info("%s: int2ap == INT_POWERSAFE_FAIL\n", ld->name); - goto exit_isr; - } - - if (ld->mdm_data->modem_type == QC_MDM6600) { - if ((int2ap == 0x1234)|(int2ap == 0xDBAB)|(int2ap == 0xABCD)) { - if (dpld->dpctl->dload_cmd_hdlr) - dpld->dpctl->dload_cmd_hdlr(dpld->dpctl, - int2ap); - goto exit_isr; - } - } - - if (UDL_CMD_VALID(int2ap)) - udl_cmd_handler(dpld, int2ap); - else if (EXT_INT_VALID(int2ap) && EXT_CMD_VALID(int2ap)) - ext_command_handler(dpld, int2ap); - else if (INT_CMD_VALID(int2ap)) - command_handler(dpld, int2ap); - else if (INT_VALID(int2ap)) - non_command_handler(dpld, int2ap); - else - mif_info("%s: ERR! invalid intr 0x%04X\n", ld->name, int2ap); + dpram_intr_handler(dpld, int2ap); -exit_isr: - dpram_allow_sleep(dpld); return IRQ_HANDLED; } -static void dpram_send_ipc(struct link_device *ld, int dev, struct sk_buff *skb) +static void dpram_send_ipc(struct link_device *ld, int dev, + struct io_device *iod, struct sk_buff *skb) { struct dpram_link_device *dpld = to_dpram_link_device(ld); - struct sk_buff_head *txq; + struct sk_buff_head *txq = ld->skb_txq[dev]; int ret; u16 mask; - if (unlikely(dev >= dpld->max_ipc_dev)) { - mif_info("%s: ERR! dev %d >= max_ipc_dev(%s)\n", - ld->name, dev, get_dev_name(dpld->max_ipc_dev)); - return; + skb_queue_tail(txq, skb); + if (txq->qlen > 1024) { + mif_debug("%s: %s txq->qlen %d > 1024\n", + ld->name, get_dev_name(dev), txq->qlen); } - if (dpram_wake_up(dpld) < 0) - return; + if (dpld->dp_type == CP_IDPRAM) { + if (dpram_wake_up(dpld) < 0) { + trigger_force_cp_crash(dpld); + return; + } + } if (!dpram_ipc_active(dpld)) goto exit; - txq = ld->skb_txq[dev]; - if (txq->qlen > 1024) - mif_info("%s: txq->qlen %d > 1024\n", ld->name, txq->qlen); - - skb_queue_tail(txq, skb); - if (atomic_read(&dpld->res_required[dev]) > 0) { mif_debug("%s: %s_TXQ is full\n", ld->name, get_dev_name(dev)); goto exit; @@ -1463,46 +1343,51 @@ static void dpram_send_ipc(struct link_device *ld, int dev, struct sk_buff *skb) ret = dpram_try_ipc_tx(dpld, dev); if (ret > 0) { - mask = dpld->dpctl->get_mask_send(dev); - dpld->dpctl->send_intr(INT_NON_CMD(mask)); - } else { - mask = dpld->dpctl->get_mask_req_ack(dev); - dpld->dpctl->send_intr(INT_NON_CMD(mask)); + mask = get_mask_send(dpld, dev); + send_intr(dpld, INT_NON_CMD(mask)); + } else if (ret == -ENOSPC) { + mask = get_mask_req_ack(dpld, dev); + send_intr(dpld, INT_NON_CMD(mask)); mif_info("%s: Send REQ_ACK 0x%04X\n", ld->name, mask); + } else { + mif_info("%s: dpram_try_ipc_tx fail (err %d)\n", ld->name, ret); } exit: - dpram_allow_sleep(dpld); + if (dpld->dp_type == CP_IDPRAM) + dpram_allow_sleep(dpld); } -static int dpram_send_binary(struct link_device *ld, struct sk_buff *skb) +static int dpram_send_cp_binary(struct link_device *ld, struct sk_buff *skb) { - int err = 0; - - if (ld->mdm_data->modem_type == SEC_CMC221) - err = cmc22x_idpram_send_main(ld, skb); - else if (ld->mdm_data->modem_type == VIA_CBP72) - err = cbp72_edpram_send_bin(ld, skb); + struct dpram_link_device *dpld = to_dpram_link_device(ld); - return err; + if (dpld->ext_op && dpld->ext_op->download_binary) + return dpld->ext_op->download_binary(dpld, skb); + else + return -ENODEV; } static int dpram_send(struct link_device *ld, struct io_device *iod, struct sk_buff *skb) { - enum dev_format fmt = iod->format; + enum dev_format dev = iod->format; int len = skb->len; - switch (fmt) { + switch (dev) { case IPC_FMT: case IPC_RAW: case IPC_RFS: - if (likely(ld->mc->phone_state == STATE_ONLINE)) - dpram_send_ipc(ld, fmt, skb); + if (likely(ld->mode == LINK_MODE_IPC)) { + dpram_send_ipc(ld, dev, iod, skb); + } else { + mif_info("%s: ld->mode != LINK_MODE_IPC\n", ld->name); + dev_kfree_skb_any(skb); + } return len; case IPC_BOOT: - return dpram_send_binary(ld, skb); + return dpram_send_cp_binary(ld, skb); default: mif_info("%s: ERR! no TXQ for %s\n", ld->name, iod->name); @@ -1511,291 +1396,252 @@ static int dpram_send(struct link_device *ld, struct io_device *iod, } } -static int dpram_set_dl_magic(struct link_device *ld, struct io_device *iod) +static int dpram_force_dump(struct link_device *ld, struct io_device *iod) { struct dpram_link_device *dpld = to_dpram_link_device(ld); - - ld->mode = LINK_MODE_DLOAD; - - iowrite32(DP_MAGIC_DMDL, dpld->dl_map.magic); - + trigger_force_cp_crash(dpld); return 0; } -static int dpram_force_dump(struct link_device *ld, struct io_device *iod) +static void dpram_dump_memory(struct link_device *ld, char *buff) { struct dpram_link_device *dpld = to_dpram_link_device(ld); + u8 __iomem *base = dpld->dpctl->dp_base; + u32 size = dpld->dpctl->dp_size; - mif_info("%s\n", ld->name); - - if (dpram_wake_up(dpld) < 0) - mif_info("%s: WARNING! dpram_wake_up fail\n", ld->name); - - dpram_trigger_force_cp_crash(dpld); - - dpram_allow_sleep(dpld); + if (dpld->dp_type == CP_IDPRAM) + dpram_wake_up(dpld); - return 0; + memcpy(buff, base, size); } -static int dpram_set_ul_magic(struct link_device *ld, struct io_device *iod) +static int dpram_dump_start(struct link_device *ld, struct io_device *iod) { struct dpram_link_device *dpld = to_dpram_link_device(ld); - u8 *dest = dpld->ul_map.buff; - - ld->mode = LINK_MODE_ULOAD; - - if (ld->mdm_data->modem_type == SEC_CMC221) { - wake_lock(&dpld->dpram_wake_lock); - dpld->dump_rcvd = 0; - iowrite32(CMC22x_CP_DUMP_MAGIC, dpld->ul_map.magic); - } else { - iowrite32(DP_MAGIC_UMDL, dpld->ul_map.magic); - iowrite8((u8)START_INDEX, dest + 0); - iowrite8((u8)0x1, dest + 1); - iowrite8((u8)0x1, dest + 2); - iowrite8((u8)0x0, dest + 3); - iowrite8((u8)END_INDEX, dest + 4); - } - - init_completion(&dpld->dump_start_complete); - - return 0; + if (dpld->ext_op && dpld->ext_op->dump_start) + return dpld->ext_op->dump_start(dpld); + else + return -ENODEV; } static int dpram_dump_update(struct link_device *ld, struct io_device *iod, unsigned long arg) { struct dpram_link_device *dpld = to_dpram_link_device(ld); - struct dpram_dump_arg dump; - int ret; - - ret = copy_from_user(&dump, (void __user *)arg, sizeof(dump)); - if (ret < 0) { - mif_info("%s: ERR! copy_from_user fail\n", ld->name); - return ret; - } - if (ld->mdm_data->modem_type == SEC_CMC221) - return cmc22x_idpram_upload(dpld, &dump); + if (dpld->ext_op && dpld->ext_op->dump_update) + return dpld->ext_op->dump_update(dpld, (void *)arg); else - return dpram_upload(dpld, &dump, (unsigned char __user *)arg); + return -ENODEV; } -static int dpram_link_ioctl(struct link_device *ld, struct io_device *iod, +static int dpram_ioctl(struct link_device *ld, struct io_device *iod, unsigned int cmd, unsigned long arg) { - int err = -EFAULT; struct dpram_link_device *dpld = to_dpram_link_device(ld); + int err = 0; mif_info("%s: cmd 0x%08X\n", ld->name, cmd); switch (cmd) { - case IOCTL_DPRAM_SEND_BOOT: - err = cmc22x_idpram_send_boot(ld, arg); - if (err < 0) { - mif_info("%s: ERR! dpram_send_boot fail\n", ld->name); - goto exit; - } - break; + case IOCTL_DPRAM_INIT_STATUS: + mif_debug("%s: get dpram init status\n", ld->name); + return dpld->dpram_init_status; - case IOCTL_DPRAM_PHONE_POWON: - if (dpld->dpctl->cpimage_load_prepare) { - err = dpld->dpctl->cpimage_load_prepare(dpld->dpctl); - if (err < 0) { - mif_info("%s: ERR! cpimage_load_prepare fail\n", - ld->name); - goto exit; - } + default: + if (dpld->ext_ioctl) { + err = dpld->ext_ioctl(dpld, iod, cmd, arg); + } else { + mif_err("%s: ERR! invalid cmd 0x%08X\n", ld->name, cmd); + err = -EINVAL; } - break; - case IOCTL_DPRAM_PHONEIMG_LOAD: - if (dpld->dpctl->cpimage_load) { - err = dpld->dpctl->cpimage_load( - (void *)arg, dpld->dpctl); - if (err < 0) { - mif_info("%s: ERR! cpimage_load fail\n", - ld->name); - goto exit; - } - } break; + } - case IOCTL_DPRAM_NVDATA_LOAD: - if (dpld->dpctl->nvdata_load) { - err = dpld->dpctl->nvdata_load( - (void *)arg, dpld->dpctl); - if (err < 0) { - mif_info("%s: ERR! nvdata_load fail\n", - ld->name); - goto exit; - } - } - break; + return err; +} - case IOCTL_DPRAM_PHONE_BOOTSTART: - if (dpld->dpctl->phone_boot_start) { - err = dpld->dpctl->phone_boot_start(dpld->dpctl); - if (err < 0) { - mif_info("%s: ERR! phone_boot_start fail\n", - ld->name); - goto exit; - } - } - if (dpld->dpctl->phone_boot_start_post_process) { - err = dpld->dpctl->phone_boot_start_post_process(); - if (err < 0) { - mif_info("%s: ERR! " - "phone_boot_start_post_process fail\n", - ld->name); - goto exit; - } - } - break; +static void dpram_remap_std_16k_region(struct dpram_link_device *dpld) +{ + struct dpram_ipc_16k_map *dpram_map; + struct dpram_ipc_device *dev; - case IOCTL_DPRAM_PHONE_UPLOAD_STEP1: - disable_irq_nosync(dpld->irq); - - if (dpld->dpctl->cpupload_step1) { - err = dpld->dpctl->cpupload_step1(dpld->dpctl); - if (err < 0) { - dpld->dpctl->clear_intr(); - enable_irq(dpld->irq); - mif_info("%s: ERR! cpupload_step1 fail\n", - ld->name); - goto exit; - } - } - break; + dpram_map = (struct dpram_ipc_16k_map *)dpld->dp_base; - case IOCTL_DPRAM_PHONE_UPLOAD_STEP2: - if (dpld->dpctl->cpupload_step2) { - err = dpld->dpctl->cpupload_step2( - (void *)arg, dpld->dpctl); - if (err < 0) { - dpld->dpctl->clear_intr(); - enable_irq(dpld->irq); - mif_info("%s: ERR! cpupload_step2 fail\n", - ld->name); - goto exit; - } - } - break; + /* magic code and access enable fields */ + dpld->ipc_map.magic = (u16 __iomem *)&dpram_map->magic; + dpld->ipc_map.access = (u16 __iomem *)&dpram_map->access; - case IOCTL_DPRAM_INIT_STATUS: - mif_debug("%s: get dpram init status\n", ld->name); - return dpld->dpram_init_status; + /* FMT */ + dev = &dpld->ipc_map.dev[IPC_FMT]; - case IOCTL_MODEM_DL_START: - err = dpram_set_dl_magic(ld, iod); - if (err < 0) { - mif_info("%s: ERR! dpram_set_dl_magic fail\n", - ld->name); - goto exit; - } + strcpy(dev->name, "FMT"); + dev->id = IPC_FMT; - default: - break; - } + dev->txq.head = (u16 __iomem *)&dpram_map->fmt_tx_head; + dev->txq.tail = (u16 __iomem *)&dpram_map->fmt_tx_tail; + dev->txq.buff = (u8 __iomem *)&dpram_map->fmt_tx_buff[0]; + dev->txq.size = DP_16K_FMT_TX_BUFF_SZ; - return 0; + dev->rxq.head = (u16 __iomem *)&dpram_map->fmt_rx_head; + dev->rxq.tail = (u16 __iomem *)&dpram_map->fmt_rx_tail; + dev->rxq.buff = (u8 __iomem *)&dpram_map->fmt_rx_buff[0]; + dev->rxq.size = DP_16K_FMT_RX_BUFF_SZ; -exit: - return err; + dev->mask_req_ack = INT_MASK_REQ_ACK_F; + dev->mask_res_ack = INT_MASK_RES_ACK_F; + dev->mask_send = INT_MASK_SEND_F; + + /* RAW */ + dev = &dpld->ipc_map.dev[IPC_RAW]; + + strcpy(dev->name, "RAW"); + dev->id = IPC_RAW; + + dev->txq.head = (u16 __iomem *)&dpram_map->raw_tx_head; + dev->txq.tail = (u16 __iomem *)&dpram_map->raw_tx_tail; + dev->txq.buff = (u8 __iomem *)&dpram_map->raw_tx_buff[0]; + dev->txq.size = DP_16K_RAW_TX_BUFF_SZ; + + dev->rxq.head = (u16 __iomem *)&dpram_map->raw_rx_head; + dev->rxq.tail = (u16 __iomem *)&dpram_map->raw_rx_tail; + dev->rxq.buff = (u8 __iomem *)&dpram_map->raw_rx_buff[0]; + dev->rxq.size = DP_16K_RAW_RX_BUFF_SZ; + + dev->mask_req_ack = INT_MASK_REQ_ACK_R; + dev->mask_res_ack = INT_MASK_RES_ACK_R; + dev->mask_send = INT_MASK_SEND_R; + + /* interrupt ports */ + dpld->ipc_map.mbx_cp2ap = (u16 __iomem *)&dpram_map->mbx_cp2ap; + dpld->ipc_map.mbx_ap2cp = (u16 __iomem *)&dpram_map->mbx_ap2cp; } -static void dpram_table_init(struct dpram_link_device *dpld) +static int dpram_table_init(struct dpram_link_device *dpld) { - struct link_device *ld; + struct link_device *ld = &dpld->ld; u8 __iomem *dp_base; - - if (!dpld) { - mif_info("ERR! dpld == NULL\n"); - return; - } - ld = &dpld->ld; + int i; if (!dpld->dp_base) { mif_info("%s: ERR! dpld->dp_base == NULL\n", ld->name); - return; + return -EINVAL; } - dp_base = dpld->dp_base; + /* Map for IPC */ + if (dpld->dpctl->ipc_map) { + memcpy(&dpld->ipc_map, dpld->dpctl->ipc_map, + sizeof(struct dpram_ipc_map)); + } else { + if (dpld->dp_size == DPRAM_SIZE_16KB) + dpram_remap_std_16k_region(dpld); + else + return -EINVAL; + } + + dpld->magic = dpld->ipc_map.magic; + dpld->access = dpld->ipc_map.access; + for (i = 0; i < dpld->max_ipc_dev; i++) + dpld->dev[i] = &dpld->ipc_map.dev[i]; + dpld->mbx2ap = dpld->ipc_map.mbx_cp2ap; + dpld->mbx2cp = dpld->ipc_map.mbx_ap2cp; + /* Map for booting */ - if (ld->mdm_data->modem_type == SEC_CMC221) { - dpld->bt_map.buff = (u8 *)(dp_base); - dpld->bt_map.req = (u32 *)(dp_base + DP_BOOT_REQ_OFFSET); - dpld->bt_map.resp = (u32 *)(dp_base + DP_BOOT_RESP_OFFSET); - dpld->bt_map.size = dpld->dp_size; - } else if (ld->mdm_data->modem_type == QC_MDM6600) { - if (dpld->dpctl->bt_map_init) - dpld->dpctl->bt_map_init(dpld->dpctl); - } else if (ld->mdm_data->modem_type == VIA_CBP72) { + if (dpld->ext_op && dpld->ext_op->init_boot_map) { + dpld->ext_op->init_boot_map(dpld); + } else { dpld->bt_map.magic = (u32 *)(dp_base); dpld->bt_map.buff = (u8 *)(dp_base + DP_BOOT_BUFF_OFFSET); - dpld->bt_map.size = dpld->dp_size - 4; - } else { - dpld->bt_map.buff = (u8 *)(dp_base); - dpld->bt_map.req = (u32 *)(dp_base + DP_BOOT_REQ_OFFSET); - dpld->bt_map.resp = (u32 *)(dp_base + DP_BOOT_RESP_OFFSET); - dpld->bt_map.size = dpld->dp_size - 4; + dpld->bt_map.size = dpld->dp_size - 8; } /* Map for download (FOTA, UDL, etc.) */ - if (ld->mdm_data->modem_type == SEC_CMC221 || - ld->mdm_data->modem_type == VIA_CBP72) { + if (dpld->ext_op && dpld->ext_op->init_dl_map) { + dpld->ext_op->init_dl_map(dpld); + } else { dpld->dl_map.magic = (u32 *)(dp_base); - dpld->dl_map.buff = (u8 *)(dp_base + DP_DLOAD_BUFF_OFFSET); + dpld->dl_map.buff = (u8 *)(dp_base + DP_DLOAD_BUFF_OFFSET); } /* Map for upload mode */ - dpld->ul_map.magic = (u32 *)(dp_base); - if (ld->mdm_data->modem_type == SEC_CMC221) - dpld->ul_map.buff = (u8 *)(dp_base); - else + if (dpld->ext_op && dpld->ext_op->init_ul_map) { + dpld->ext_op->init_ul_map(dpld); + } else { + dpld->ul_map.magic = (u32 *)(dp_base); dpld->ul_map.buff = (u8 *)(dp_base + DP_ULOAD_BUFF_OFFSET); + } + + return 0; } -#if defined(CONFIG_MACH_M0_CTC) -static void dpram_link_terminate(struct link_device *ld, struct io_device *iod) +static void dpram_setup_common_op(struct dpram_link_device *dpld) { - struct dpram_link_device *dpld = to_dpram_link_device(ld); + dpld->clear_intr = clear_intr; + dpld->recv_intr = recv_intr; + dpld->send_intr = send_intr; + dpld->get_magic = get_magic; + dpld->set_magic = set_magic; + dpld->get_access = get_access; + dpld->set_access = set_access; + dpld->get_tx_head = get_tx_head; + dpld->get_tx_tail = get_tx_tail; + dpld->set_tx_head = set_tx_head; + dpld->set_tx_tail = set_tx_tail; + dpld->get_tx_buff = get_tx_buff; + dpld->get_tx_buff_size = get_tx_buff_size; + dpld->get_rx_head = get_rx_head; + dpld->get_rx_tail = get_rx_tail; + dpld->set_rx_head = set_rx_head; + dpld->set_rx_tail = set_rx_tail; + dpld->get_rx_buff = get_rx_buff; + dpld->get_rx_buff_size = get_rx_buff_size; + dpld->get_mask_req_ack = get_mask_req_ack; + dpld->get_mask_res_ack = get_mask_res_ack; + dpld->get_mask_send = get_mask_send; + dpld->ipc_rx_handler = dpram_ipc_rx; +} - mif_info("dpram_link_terminate\n"); +static int dpram_link_init(struct link_device *ld, struct io_device *iod) +{ + return 0; +} - if (dpld->dpctl->terminate_link) - dpld->dpctl->terminate_link(dpld->dpctl); +static void dpram_link_terminate(struct link_device *ld, struct io_device *iod) +{ + return; } -#endif struct link_device *dpram_create_link_device(struct platform_device *pdev) { + struct modem_data *mdm_data = NULL; struct dpram_link_device *dpld = NULL; struct link_device *ld = NULL; - struct modem_data *pdata = NULL; + struct resource *res = NULL; + resource_size_t res_size; struct modemlink_dpram_control *dpctl = NULL; + unsigned long task_data; int ret = 0; int i = 0; int bsize; int qsize; - char wq_name[32]; - char wq_suffix[32]; /* Get the platform data */ - pdata = (struct modem_data *)pdev->dev.platform_data; - if (!pdata) { - mif_info("ERR! pdata == NULL\n"); + mdm_data = (struct modem_data *)pdev->dev.platform_data; + if (!mdm_data) { + mif_info("ERR! mdm_data == NULL\n"); goto err; } - if (!pdata->dpram_ctl) { - mif_info("ERR! pdata->dpram_ctl == NULL\n"); + mif_info("modem = %s\n", mdm_data->name); + mif_info("link device = %s\n", mdm_data->link_name); + + if (!mdm_data->dpram_ctl) { + mif_info("ERR! mdm_data->dpram_ctl == NULL\n"); goto err; } - mif_info("link device = %s\n", pdata->link_name); - mif_info("modem = %s\n", pdata->name); + dpctl = mdm_data->dpram_ctl; /* Alloc DPRAM link device structure */ dpld = kzalloc(sizeof(struct dpram_link_device), GFP_KERNEL); @@ -1805,25 +1651,37 @@ struct link_device *dpram_create_link_device(struct platform_device *pdev) } ld = &dpld->ld; - /* Extract modem data and DPRAM control data from the platform data */ - ld->mdm_data = pdata; - ld->name = pdata->link_name; - ld->ipc_version = pdata->ipc_version; + /* Retrieve modem data and DPRAM control data from the modem data */ + ld->mdm_data = mdm_data; + ld->name = mdm_data->link_name; + ld->ipc_version = mdm_data->ipc_version; - /* Set attributes as a link device */ - ld->aligned = pdata->dpram_ctl->aligned; - if (ld->aligned) - mif_info("%s: ld->aligned == TRUE\n", ld->name); + /* Retrieve the most basic data for IPC from the modem data */ + dpld->dpctl = dpctl; + dpld->dp_type = dpctl->dp_type; + if (mdm_data->ipc_version < SIPC_VER_50) { + if (!dpctl->max_ipc_dev) { + mif_info("ERR! no max_ipc_dev\n"); + goto err; + } + + ld->aligned = dpctl->aligned; + dpld->max_ipc_dev = dpctl->max_ipc_dev; + } else { + ld->aligned = 1; + dpld->max_ipc_dev = MAX_SIPC5_DEV; + } + + /* Set attributes as a link device */ + ld->init_comm = dpram_link_init; + ld->terminate_comm = dpram_link_terminate; ld->send = dpram_send; ld->force_dump = dpram_force_dump; - ld->dump_start = dpram_set_ul_magic; + ld->dump_start = dpram_dump_start; ld->dump_update = dpram_dump_update; - ld->ioctl = dpram_link_ioctl; + ld->ioctl = dpram_ioctl; -#if defined(CONFIG_MACH_M0_CTC) - ld->terminate_comm = dpram_link_terminate; -#endif INIT_LIST_HEAD(&ld->list); skb_queue_head_init(&ld->sk_fmt_tx_q); @@ -1833,54 +1691,68 @@ struct link_device *dpram_create_link_device(struct platform_device *pdev) ld->skb_txq[IPC_RAW] = &ld->sk_raw_tx_q; ld->skb_txq[IPC_RFS] = &ld->sk_rfs_tx_q; - /* Set attributes as a dpram link device */ - dpctl = pdata->dpram_ctl; - dpld->dpctl = dpctl; + /* Set up function pointers */ + dpram_setup_common_op(dpld); + dpld->dpram_dump = dpram_dump_memory; + dpld->ext_op = dpram_get_ext_op(mdm_data->modem_type); + if (dpld->ext_op && dpld->ext_op->ioctl) + dpld->ext_ioctl = dpld->ext_op->ioctl; + + /* Retrieve DPRAM resource */ + if (!dpctl->dp_base) { + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + mif_info("%s: ERR! platform_get_resource fail\n", + ld->name); + goto err; + } + res_size = resource_size(res); + dpctl->dp_base = ioremap_nocache(res->start, res_size); + dpctl->dp_size = res_size; + } dpld->dp_base = dpctl->dp_base; dpld->dp_size = dpctl->dp_size; - dpld->dp_type = dpctl->dp_type; - dpld->max_ipc_dev = dpctl->max_ipc_dev; + mif_info("%s: dp_type %d, aligned %d, dp_base 0x%08X, dp_size %d\n", + ld->name, dpld->dp_type, ld->aligned, (int)dpld->dp_base, + dpld->dp_size); - dpld->irq = dpctl->dpram_irq; - if (dpld->irq < 0) { - mif_info("%s: ERR! failed to get IRQ#\n", ld->name); + /* Initialize DPRAM map (physical map -> logical map) */ + ret = dpram_table_init(dpld); + if (ret < 0) { + mif_info("%s: ERR! dpram_table_init fail (err %d)\n", + ld->name, ret); goto err; } - mif_info("%s: DPRAM IRQ# = %d\n", ld->name, dpld->irq); - wake_lock_init(&dpld->dpram_wake_lock, WAKE_LOCK_SUSPEND, - dpctl->dpram_wlock_name); + /* Disable IPC */ + set_magic(dpld, 0); + set_access(dpld, 0); + dpld->dpram_init_status = DPRAM_INIT_STATE_NONE; + + /* Initialize locks, completions, and bottom halves */ + snprintf(dpld->wlock_name, DP_MAX_NAME_LEN, "%s_wlock", ld->name); + wake_lock_init(&dpld->wlock, WAKE_LOCK_SUSPEND, dpld->wlock_name); init_completion(&dpld->dpram_init_cmd); init_completion(&dpld->modem_pif_init_done); init_completion(&dpld->udl_start_complete); init_completion(&dpld->udl_cmd_complete); - init_completion(&dpld->crash_start_complete); init_completion(&dpld->dump_start_complete); init_completion(&dpld->dump_recv_done); - spin_lock_init(&dpld->tx_lock); - - tasklet_init(&dpld->rx_tsk, dpram_ipc_rx_task, (unsigned long)dpld); - - /* Initialize DPRAM map (physical map -> logical map) */ - dpram_table_init(dpld); + task_data = (unsigned long)dpld; + tasklet_init(&dpld->rx_tsk, dpram_ipc_rx_task, task_data); -#if 0 - dpld->magic = dpctl->ipc_map->magic; - dpld->access = dpctl->ipc_map->access; + /* Prepare SKB queue head for RX processing */ for (i = 0; i < dpld->max_ipc_dev; i++) - dpld->dev[i] = &dpctl->ipc_map->dev[i]; - dpld->mbx2ap = dpctl->ipc_map->mbx_cp2ap; - dpld->mbx2cp = dpctl->ipc_map->mbx_ap2cp; -#endif + skb_queue_head_init(&dpld->skb_rxq[i]); - /* Prepare rxb queue */ + /* Prepare RXB queue */ qsize = DPRAM_MAX_RXBQ_SIZE; for (i = 0; i < dpld->max_ipc_dev; i++) { - bsize = rxbq_get_page_size(dpctl->get_rx_buff_size(i)); + bsize = rxbq_get_page_size(get_rx_buff_size(dpld, i)); dpld->rxbq[i].size = qsize; dpld->rxbq[i].in = 0; dpld->rxbq[i].out = 0; @@ -1894,30 +1766,49 @@ struct link_device *dpram_create_link_device(struct platform_device *pdev) ld->name, get_dev_name(i), bsize, qsize); } - /* Prepare a clean buffer */ + /* Prepare a multi-purpose miscellaneous buffer */ dpld->buff = kzalloc(dpld->dp_size, GFP_KERNEL); if (!dpld->buff) { mif_info("%s: ERR! kzalloc dpld->buff fail\n", ld->name); goto err; } - if (ld->mdm_data->modem_type == QC_MDM6600) { - if (dpctl->load_init) - dpctl->load_init(dpctl); + /* Retrieve DPRAM IRQ GPIO# */ + dpld->gpio_dpram_int = mdm_data->gpio_dpram_int; + + /* Retrieve DPRAM IRQ# */ + if (!dpctl->dpram_irq) { + dpctl->dpram_irq = platform_get_irq_byname(pdev, "dpram_irq"); + if (dpctl->dpram_irq < 0) { + mif_info("%s: ERR! platform_get_irq_byname fail\n", + ld->name); + goto err; + } } + dpld->irq = dpctl->dpram_irq; - /* Disable IPC */ - dpctl->set_magic(0); - dpctl->set_access(0); - dpld->dpram_init_status = DPRAM_INIT_STATE_NONE; + /* Retrieve DPRAM IRQ flags */ + if (!dpctl->dpram_irq_flags) + dpctl->dpram_irq_flags = (IRQF_NO_SUSPEND | IRQF_TRIGGER_LOW); + dpld->irq_flags = dpctl->dpram_irq_flags; /* Register DPRAM interrupt handler */ - ret = dpram_register_isr(dpld->irq, dpram_irq_handler, - dpctl->dpram_irq_flags, dpctl->dpram_irq_name, ld); + snprintf(dpld->irq_name, DP_MAX_NAME_LEN, "%s_irq", ld->name); + if (dpld->ext_op && dpld->ext_op->irq_handler) + dpld->irq_handler = dpld->ext_op->irq_handler; + else if (dpld->dp_type == CP_IDPRAM) + dpld->irq_handler = cp_idpram_irq_handler; + else if (dpld->dp_type == AP_IDPRAM) + dpld->irq_handler = ap_idpram_irq_handler; + else + dpld->irq_handler = ext_dpram_irq_handler; + + ret = dpram_register_isr(dpld->irq, dpld->irq_handler, dpld->irq_flags, + dpld->irq_name, dpld); if (ret) goto err; - - return ld; + else + return ld; err: if (dpld) { diff --git a/drivers/misc/modem_if/modem_link_device_dpram.h b/drivers/misc/modem_if/modem_link_device_dpram.h index d61da83..c651e51 100644 --- a/drivers/misc/modem_if/modem_link_device_dpram.h +++ b/drivers/misc/modem_if/modem_link_device_dpram.h @@ -23,44 +23,61 @@ #include "modem_prj.h" -/* for DPRAM hostboot */ -#define CMC22x_AP_BOOT_DOWN_DONE 0x54329876 -#define CMC22x_CP_REQ_MAIN_BIN 0xA5A5A5A5 -#define CMC22x_CP_REQ_NV_DATA 0x5A5A5A5A -#define CMC22x_CP_DUMP_MAGIC 0xDEADDEAD - -#define CMC22x_HOST_DOWN_START 0x1234 -#define CMC22x_HOST_DOWN_END 0x4321 -#define CMC22x_REG_NV_DOWN_END 0xABCD -#define CMC22x_CAL_NV_DOWN_END 0xDCBA - -#define CMC22x_1ST_BUFF_READY 0xAAAA -#define CMC22x_2ND_BUFF_READY 0xBBBB -#define CMC22x_1ST_BUFF_FULL 0x1111 -#define CMC22x_2ND_BUFF_FULL 0x2222 - -#define CMC22x_CP_RECV_NV_END 0x8888 -#define CMC22x_CP_CAL_OK 0x4F4B -#define CMC22x_CP_CAL_BAD 0x4552 -#define CMC22x_CP_DUMP_END 0xFADE - -#define CMC22x_DUMP_BUFF_SIZE 8192 /* 8 KB */ -#define CMC22x_DUMP_WAIT_TIMEOVER 1 /* 1 ms */ +#define DPRAM_MAGIC_CODE 0xAA /* interrupt masks.*/ -#define INT_MASK_VALID 0x0080 -#define INT_MASK_CMD 0x0040 -#define INT_VALID(x) ((x) & INT_MASK_VALID) +#define INT_MASK_VALID 0x0080 +#define INT_MASK_CMD 0x0040 +#define INT_VALID(x) ((x) & INT_MASK_VALID) #define INT_CMD_VALID(x) ((x) & INT_MASK_CMD) -#define INT_NON_CMD(x) (INT_MASK_VALID | (x)) -#define INT_CMD(x) (INT_MASK_VALID | INT_MASK_CMD | (x)) +#define INT_NON_CMD(x) (INT_MASK_VALID | (x)) +#define INT_CMD(x) (INT_MASK_VALID | INT_MASK_CMD | (x)) +#define EXT_UDL_MASK 0xF000 +#define EXT_UDL_CMD(x) ((x) & EXT_UDL_MASK) #define EXT_INT_VALID_MASK 0x8000 #define EXT_CMD_VALID_MASK 0x4000 +#define UDL_CMD_VALID_MASK 0x2000 #define EXT_INT_VALID(x) ((x) & EXT_INT_VALID_MASK) #define EXT_CMD_VALID(x) ((x) & EXT_CMD_VALID_MASK) +#define UDL_CMD_VALID(x) ((x) & UDL_CMD_VALID_MASK) #define INT_EXT_CMD(x) (EXT_INT_VALID_MASK | EXT_CMD_VALID_MASK | (x)) +#define EXT_CMD_MASK(x) ((x) & 0x0FFF) +#define EXT_CMD_SET_SPEED_LOW 0x0011 +#define EXT_CMD_SET_SPEED_MID 0x0012 +#define EXT_CMD_SET_SPEED_HIGH 0x0013 + +#define UDL_RESULT_SUCCESS 0x1 +#define UDL_RESULT_FAIL 0x2 + +#define UDL_CMD_MASK(x) (((x) >> 8) & 0xF) +#define UDL_CMD_RECV_READY 0x1 +#define UDL_CMD_DL_START_REQ 0x2 +#define UDL_CMD_DL_START_RESP 0x3 +#define UDL_CMD_IMAGE_SEND_REQ 0x4 +#define UDL_CMD_SEND_DONE_RESP 0x5 +#define UDL_CMD_SEND_DONE_REQ 0x6 +#define UDL_CMD_UPDATE_DONE 0x7 +#define UDL_CMD_STATUS_UPDATE 0x8 +#define UDL_CMD_IMAGE_SEND_RESP 0x9 +#define UDL_CMD_EFS_CLEAR_RESP 0xB +#define UDL_CMD_ALARM_BOOT_OK 0xC +#define UDL_CMD_ALARM_BOOT_FAIL 0xD + +#define CMD_IMG_START_REQ 0x9200 +#define CMD_IMG_SEND_REQ 0x9400 +#define CMD_DL_SEND_DONE_REQ 0x9600 +#define CMD_UL_RECV_RESP 0x9601 +#define CMD_UL_RECV_DONE_RESP 0x9801 + +/* special interrupt cmd indicating modem boot failure. */ +#define INT_POWERSAFE_FAIL 0xDEAD + +#define INT_MASK_REQ_ACK_RFS 0x0400 /* Request RES_ACK_RFS */ +#define INT_MASK_RES_ACK_RFS 0x0200 /* Response of REQ_ACK_RFS */ +#define INT_MASK_SEND_RFS 0x0100 /* Indicate sending RFS data */ + #define INT_MASK_REQ_ACK_F 0x0020 #define INT_MASK_REQ_ACK_R 0x0010 #define INT_MASK_RES_ACK_F 0x0008 @@ -68,10 +85,6 @@ #define INT_MASK_SEND_F 0x0002 #define INT_MASK_SEND_R 0x0001 -#define INT_MASK_REQ_ACK_RFS 0x0400 /* Request RES_ACK_RFS */ -#define INT_MASK_RES_ACK_RFS 0x0200 /* Response of REQ_ACK_RFS */ -#define INT_MASK_SEND_RFS 0x0100 /* Indicate sending RFS data */ - #define INT_MASK_RES_ACK_SET \ (INT_MASK_RES_ACK_F | INT_MASK_RES_ACK_R | INT_MASK_RES_ACK_RFS) @@ -95,59 +108,26 @@ #define INT_CMD_SILENT_NV_REBUILDING 0xE #define INT_CMD_NORMAL_PWR_OFF 0xF -#define EXT_CMD_MASK(x) ((x) & 0x3FFF) -#define EXT_CMD_SET_SPEED_LOW 0x0011 -#define EXT_CMD_SET_SPEED_MID 0x0012 -#define EXT_CMD_SET_SPEED_HIGH 0x0013 - -/* special interrupt cmd indicating modem boot failure. */ -#define INT_POWERSAFE_FAIL 0xDEAD - -#define UDL_CMD_VALID(x) (((x) & 0xA000) == 0xA000) -#define UDL_RESULT_FAIL 0x2 -#define UDL_RESULT_SUCCESS 0x1 -#define UDL_CMD_MASK(x) (((x) >> 8) & 0xF) -#define UDL_CMD_RECEIVE_READY 0x1 -#define UDL_CMD_DOWNLOAD_START_REQ 0x2 -#define UDL_CMD_DOWNLOAD_START_RESP 0x3 -#define UDL_CMD_IMAGE_SEND_REQ 0x4 -/* change dpram download flow */ -#define UDL_CMD_IMAGE_SEND_RESP 0x9 - -#define UDL_CMD_SEND_DONE_RESP 0x5 -#define UDL_CMD_SEND_DONE_REQ 0x6 -#define UDL_CMD_UPDATE_DONE 0x7 - -#define UDL_CMD_STATUS_UPDATE 0x8 -#define UDL_CMD_EFS_CLEAR_RESP 0xB -#define UDL_CMD_ALARM_BOOT_OK 0xC -#define UDL_CMD_ALARM_BOOT_FAIL 0xD - -#define CMD_IMG_START_REQ 0x9200 -#define CMD_IMG_SEND_REQ 0x9400 -#define CMD_DL_SEND_DONE_REQ 0x9600 -#define CMD_UL_RECEIVE_RESP 0x9601 -#define CMD_UL_RECEIVE_DONE_RESP 0x9801 - -#define START_INDEX 0x7F -#define END_INDEX 0x7E - -#define DP_MAGIC_DMDL 0x4445444C -#define DP_MAGIC_UMDL 0x4445444D -#define DP_DPRAM_SIZE 0x4000 -#define DP_DEFAULT_WRITE_LEN 8168 -#define DP_DEFAULT_DUMP_LEN 16128 -#define DP_DUMP_HEADER_SIZE 7 - -#define UDL_TIMEOUT (50 * HZ) -#define UDL_SEND_TIMEOUT (200 * HZ) -#define FORCE_CRASH_TIMEOUT (3 * HZ) -#define DUMP_TIMEOUT (30 * HZ) -#define DUMP_START_TIMEOUT (100 * HZ) - -enum cmc22x_boot_mode { - CMC22x_BOOT_MODE_NORMAL, - CMC22x_BOOT_MODE_DUMP, +#define START_FLAG 0x7F +#define END_FLAG 0x7E + +#define DP_MAGIC_DMDL 0x4445444C +#define DP_MAGIC_UMDL 0x4445444D +#define DP_DPRAM_SIZE 0x4000 +#define DP_DEFAULT_WRITE_LEN 8168 +#define DP_DEFAULT_DUMP_LEN 16128 +#define DP_DUMP_HEADER_SIZE 7 + +#define UDL_TIMEOUT (50 * HZ) +#define UDL_SEND_TIMEOUT (200 * HZ) +#define FORCE_CRASH_ACK_TIMEOUT (5 * HZ) +#define DUMP_TIMEOUT (30 * HZ) +#define DUMP_START_TIMEOUT (100 * HZ) +#define DUMP_WAIT_TIMEOUT (HZ >> 10) /* 1/1024 second */ + +enum host_boot_mode { + HOST_BOOT_MODE_NORMAL, + HOST_BOOT_MODE_DUMP, }; enum dpram_init_status { @@ -158,40 +138,27 @@ enum dpram_init_status { struct dpram_boot_img { char *addr; int size; - enum cmc22x_boot_mode mode; + enum host_boot_mode mode; unsigned req; unsigned resp; }; #define MAX_PAYLOAD_SIZE 0x2000 struct dpram_boot_frame { - unsigned request; /* AP to CP Message */ - unsigned response; /* CP to AP Response */ - ssize_t len; /* request size*/ - unsigned offset; /* offset to write */ + unsigned req; /* AP->CP request */ + unsigned resp; /* response expected by AP */ + ssize_t len; /* data size in the buffer */ + unsigned offset; /* offset to write into DPRAM */ char data[MAX_PAYLOAD_SIZE]; }; /* buffer type for modem image */ struct dpram_dump_arg { - char *buff; - int buff_size;/* AP->CP: Buffer size */ - unsigned req; /* AP->CP request */ - unsigned resp; /* CP->AP response */ - bool cmd; /* AP->CP command */ -}; - -struct dpram_firmware { - char *firmware; - int size; - int is_delta; -}; -enum dpram_link_mode { - DPRAM_LINK_MODE_INVALID = 0, - DPRAM_LINK_MODE_IPC, - DPRAM_LINK_MODE_BOOT, - DPRAM_LINK_MODE_DLOAD, - DPRAM_LINK_MODE_ULOAD, + char *buff; /* pointer to the buffer */ + int buff_size; /* buffer size */ + unsigned req; /* AP->CP request */ + unsigned resp; /* CP->AP response */ + bool cmd; /* AP->CP command */ }; struct dpram_boot_map { @@ -202,6 +169,13 @@ struct dpram_boot_map { u32 size; }; +struct qc_dpram_boot_map { + u8 __iomem *buff; + u16 __iomem *frame_size; + u16 __iomem *tag; + u16 __iomem *count; +}; + struct dpram_dload_map { u32 __iomem *magic; u8 __iomem *buff; @@ -212,20 +186,54 @@ struct dpram_uload_map { u8 __iomem *buff; }; -struct dpram_ota_header { - u8 start_index; - u16 nframes; - u16 curframe; - u16 len; - -} __packed; - struct ul_header { u8 bop; u16 total_frame; u16 curr_frame; u16 len; } __packed; + +struct dpram_udl_param { + unsigned char *addr; + unsigned int size; + unsigned int count; + unsigned int tag; +}; + +struct dpram_udl_check { + unsigned int total_size; + unsigned int rest_size; + unsigned int send_size; + unsigned int copy_start; + unsigned int copy_complete; + unsigned int boot_complete; +}; + +#define DP_BOOT_BUFF_OFFSET 4 +#define DP_DLOAD_BUFF_OFFSET 4 +#define DP_ULOAD_BUFF_OFFSET 4 +#define DP_BOOT_REQ_OFFSET 0 +#define DP_BOOT_RESP_OFFSET 8 + +#define MAX_WQ_NAME_LENGTH 64 + +#define DPRAM_MAX_RXBQ_SIZE 256 + +struct dpram_rxb { + u8 *buff; + unsigned size; + + u8 *data; + unsigned len; +}; + +struct dpram_rxb_queue { + int size; + int in; + int out; + struct dpram_rxb *rxb; +}; + /* magic_code + access_enable + @@ -245,89 +253,72 @@ struct ul_header { 2 = 16384 */ +#define DP_16K_FMT_TX_BUFF_SZ 1336 +#define DP_16K_RAW_TX_BUFF_SZ 4564 +#define DP_16K_FMT_RX_BUFF_SZ 1336 +#define DP_16K_RAW_RX_BUFF_SZ 9124 -#define SIPC5_DP_FMT_TX_BUFF_SZ 1336 -#define SIPC5_DP_RAW_TX_BUFF_SZ 4564 -#define SIPC5_DP_FMT_RX_BUFF_SZ 1336 -#define SIPC5_DP_RAW_RX_BUFF_SZ 9124 - -struct sipc5_dpram_ipc_cfg { +struct dpram_ipc_16k_map { u16 magic; u16 access; u16 fmt_tx_head; u16 fmt_tx_tail; - u8 fmt_tx_buff[SIPC5_DP_FMT_TX_BUFF_SZ]; + u8 fmt_tx_buff[DP_16K_FMT_TX_BUFF_SZ]; u16 raw_tx_head; u16 raw_tx_tail; - u8 raw_tx_buff[SIPC5_DP_RAW_TX_BUFF_SZ]; + u8 raw_tx_buff[DP_16K_RAW_TX_BUFF_SZ]; u16 fmt_rx_head; u16 fmt_rx_tail; - u8 fmt_rx_buff[SIPC5_DP_FMT_RX_BUFF_SZ]; + u8 fmt_rx_buff[DP_16K_FMT_RX_BUFF_SZ]; u16 raw_rx_head; u16 raw_rx_tail; - u8 raw_rx_buff[SIPC5_DP_RAW_RX_BUFF_SZ]; + u8 raw_rx_buff[DP_16K_RAW_RX_BUFF_SZ]; u16 mbx_cp2ap; u16 mbx_ap2cp; }; -#define DPRAM_MAX_SKB_SIZE 3072 /* 3 KB */ +#define DP_MAX_NAME_LEN 32 -#define DP_BOOT_REQ_OFFSET 0 -#define DP_BOOT_BUFF_OFFSET 4 -#define DP_BOOT_RESP_OFFSET 8 -#define DP_DLOAD_BUFF_OFFSET 4 -#define DP_ULOAD_BUFF_OFFSET 4 - -#define MAX_WQ_NAME_LENGTH 64 - -#define DPRAM_MAX_RXBQ_SIZE 256 - -struct dpram_rxb { - u8 *buff; - unsigned size; - - u8 *data; - unsigned len; -}; - -struct dpram_rxb_queue { - int size; - int in; - int out; - struct dpram_rxb *rxb; -}; +struct dpram_ext_op; struct dpram_link_device { struct link_device ld; - /* The mode of this DPRAM link device */ - enum dpram_link_mode mode; - /* DPRAM address and size */ - u32 dp_size; /* DPRAM size */ u8 __iomem *dp_base; /* DPRAM base virtual address */ + u32 dp_size; /* DPRAM size */ enum dpram_type dp_type; /* DPRAM type */ + /* DPRAM IRQ GPIO# */ + unsigned gpio_dpram_int; + /* DPRAM IRQ from CP */ int irq; + unsigned long irq_flags; + char irq_name[DP_MAX_NAME_LEN]; /* Link to DPRAM control functions dependent on each platform */ int max_ipc_dev; struct modemlink_dpram_control *dpctl; /* Physical configuration -> logical configuration */ - struct dpram_boot_map bt_map; + union { + struct dpram_boot_map bt_map; + struct qc_dpram_boot_map qc_bt_map; + }; + struct dpram_dload_map dl_map; struct dpram_uload_map ul_map; /* IPC device map */ - struct dpram_ipc_map *ipc_map; + struct dpram_ipc_map ipc_map; + /* Pointers (aliases) to IPC device map */ u16 __iomem *magic; u16 __iomem *access; struct dpram_ipc_device *dev[MAX_IPC_DEV]; @@ -335,45 +326,116 @@ struct dpram_link_device { u16 __iomem *mbx2cp; /* Wakelock for DPRAM device */ - struct wake_lock dpram_wake_lock; + struct wake_lock wlock; + char wlock_name[DP_MAX_NAME_LEN]; /* For booting */ + unsigned boot_start_complete; struct completion dpram_init_cmd; struct completion modem_pif_init_done; /* For UDL */ + struct tasklet_struct ul_tsk; + struct tasklet_struct dl_tsk; struct completion udl_start_complete; struct completion udl_cmd_complete; + struct dpram_udl_check udl_check; + struct dpram_udl_param udl_param; /* For CP RAM dump */ - struct completion crash_start_complete; + struct timer_list crash_ack_timer; struct completion dump_start_complete; struct completion dump_recv_done; struct timer_list dump_timer; int dump_rcvd; /* Count of dump packets received */ - /* For locking Tx process */ - spinlock_t tx_lock; + /* For locking TX process */ + spinlock_t tx_lock[MAX_IPC_DEV]; /* For efficient RX process */ struct tasklet_struct rx_tsk; struct dpram_rxb_queue rxbq[MAX_IPC_DEV]; - - /* For wake-up/sleep control */ - atomic_t accessing; + struct io_device *iod[MAX_IPC_DEV]; + bool use_skb; + struct sk_buff_head skb_rxq[MAX_IPC_DEV]; /* For retransmission after buffer full state */ atomic_t res_required[MAX_IPC_DEV]; + /* For wake-up/sleep control */ + atomic_t accessing; + /* Multi-purpose miscellaneous buffer */ u8 *buff; /* DPRAM IPC initialization status */ int dpram_init_status; + + /* Alias to device-specific IOCTL function */ + int (*ext_ioctl)(struct dpram_link_device *dpld, struct io_device *iod, + unsigned int cmd, unsigned long arg); + + /* Alias to DPRAM IRQ handler */ + irqreturn_t (*irq_handler)(int irq, void *data); + + /* For DPRAM dump */ + void (*dpram_dump)(struct link_device *ld, char *buff); + + /* Common operations for each DPRAM */ + void (*clear_intr)(struct dpram_link_device *dpld); + u16 (*recv_intr)(struct dpram_link_device *dpld); + void (*send_intr)(struct dpram_link_device *dpld, u16 mask); + u16 (*get_magic)(struct dpram_link_device *dpld); + void (*set_magic)(struct dpram_link_device *dpld, u16 value); + u16 (*get_access)(struct dpram_link_device *dpld); + void (*set_access)(struct dpram_link_device *dpld, u16 value); + u32 (*get_tx_head)(struct dpram_link_device *dpld, int id); + u32 (*get_tx_tail)(struct dpram_link_device *dpld, int id); + void (*set_tx_head)(struct dpram_link_device *dpld, int id, u32 head); + void (*set_tx_tail)(struct dpram_link_device *dpld, int id, u32 tail); + u8 *(*get_tx_buff)(struct dpram_link_device *dpld, int id); + u32 (*get_tx_buff_size)(struct dpram_link_device *dpld, int id); + u32 (*get_rx_head)(struct dpram_link_device *dpld, int id); + u32 (*get_rx_tail)(struct dpram_link_device *dpld, int id); + void (*set_rx_head)(struct dpram_link_device *dpld, int id, u32 head); + void (*set_rx_tail)(struct dpram_link_device *dpld, int id, u32 tail); + u8 *(*get_rx_buff)(struct dpram_link_device *dpld, int id); + u32 (*get_rx_buff_size)(struct dpram_link_device *dpld, int id); + u16 (*get_mask_req_ack)(struct dpram_link_device *dpld, int id); + u16 (*get_mask_res_ack)(struct dpram_link_device *dpld, int id); + u16 (*get_mask_send)(struct dpram_link_device *dpld, int id); + void (*ipc_rx_handler)(struct dpram_link_device *dpld, u16 int2ap); + + /* Extended operations for various modems */ + struct dpram_ext_op *ext_op; }; /* converts from struct link_device* to struct xxx_link_device* */ #define to_dpram_link_device(linkdev) \ container_of(linkdev, struct dpram_link_device, ld) +struct dpram_ext_op { + int exist; + + void (*init_boot_map)(struct dpram_link_device *dpld); + void (*init_dl_map)(struct dpram_link_device *dpld); + void (*init_ul_map)(struct dpram_link_device *dpld); + + int (*download_binary)(struct dpram_link_device *dpld, + struct sk_buff *skb); + + void (*cp_start_handler)(struct dpram_link_device *dpld); + + void (*crash_log)(struct dpram_link_device *dpld); + int (*dump_start)(struct dpram_link_device *dpld); + int (*dump_update)(struct dpram_link_device *dpld, void *arg); + + int (*ioctl)(struct dpram_link_device *dpld, struct io_device *iod, + unsigned int cmd, unsigned long arg); + + irqreturn_t (*irq_handler)(int irq, void *data); +}; + +struct dpram_ext_op *dpram_get_ext_op(enum modem_t modem); + #endif diff --git a/drivers/misc/modem_if/modem_link_device_dpram_ext_op.c b/drivers/misc/modem_if/modem_link_device_dpram_ext_op.c new file mode 100644 index 0000000..1475a46 --- /dev/null +++ b/drivers/misc/modem_if/modem_link_device_dpram_ext_op.c @@ -0,0 +1,1175 @@ +/* + * Copyright (C) 2010 Samsung Electronics. + * + * 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/irq.h> +#include <linux/gpio.h> +#include <linux/time.h> +#include <linux/interrupt.h> +#include <linux/timer.h> +#include <linux/wakelock.h> +#include <linux/delay.h> +#include <linux/wait.h> +#include <linux/sched.h> +#include <linux/vmalloc.h> +#include <linux/if_arp.h> +#include <linux/platform_device.h> +#include <linux/kallsyms.h> +#include <linux/platform_data/modem.h> + +#include "modem_prj.h" +#include "modem_link_device_dpram.h" +#include "modem_utils.h" + +#if defined(CONFIG_LTE_MODEM_CMC221) +/* +** For host (flashless) booting via DPRAM +*/ +#define CMC22x_AP_BOOT_DOWN_DONE 0x54329876 +#define CMC22x_CP_REQ_MAIN_BIN 0xA5A5A5A5 +#define CMC22x_CP_REQ_NV_DATA 0x5A5A5A5A +#define CMC22x_CP_DUMP_MAGIC 0xDEADDEAD + +#define CMC22x_HOST_DOWN_START 0x1234 +#define CMC22x_HOST_DOWN_END 0x4321 +#define CMC22x_REG_NV_DOWN_END 0xABCD +#define CMC22x_CAL_NV_DOWN_END 0xDCBA + +#define CMC22x_1ST_BUFF_READY 0xAAAA +#define CMC22x_2ND_BUFF_READY 0xBBBB +#define CMC22x_1ST_BUFF_FULL 0x1111 +#define CMC22x_2ND_BUFF_FULL 0x2222 + +#define CMC22x_CP_RECV_NV_END 0x8888 +#define CMC22x_CP_CAL_OK 0x4F4B +#define CMC22x_CP_CAL_BAD 0x4552 +#define CMC22x_CP_DUMP_END 0xFADE + +#define CMC22x_DUMP_BUFF_SIZE 8192 /* 8 KB */ +#endif + +#if defined(CONFIG_CDMA_MODEM_CBP72) +static void cbp72_init_boot_map(struct dpram_link_device *dpld) +{ + struct dpram_boot_map *bt_map = &dpld->bt_map; + + bt_map->magic = (u32 *)dpld->dp_base; + bt_map->buff = (u8 *)(dpld->dp_base + DP_BOOT_BUFF_OFFSET); + bt_map->size = dpld->dp_size - 4; +} + +static void cbp72_init_dl_map(struct dpram_link_device *dpld) +{ + dpld->dl_map.magic = (u32 *)dpld->dp_base; + dpld->dl_map.buff = (u8 *)(dpld->dp_base + DP_DLOAD_BUFF_OFFSET); +} + +static int _cbp72_edpram_wait_resp(struct dpram_link_device *dpld, u32 resp) +{ + struct link_device *ld = &dpld->ld; + int ret; + int int2cp; + + ret = wait_for_completion_interruptible_timeout( + &dpld->udl_cmd_complete, UDL_TIMEOUT); + if (!ret) { + mif_info("%s: ERR! No UDL_CMD_RESP!!!\n", ld->name); + return -ENXIO; + } + + int2cp = dpld->recv_intr(dpld); + mif_debug("%s: int2cp = 0x%x\n", ld->name, int2cp); + if (resp == int2cp || int2cp == 0xA700) + return int2cp; + else + return -EINVAL; +} + +static int _cbp72_edpram_download_bin(struct dpram_link_device *dpld, + struct sk_buff *skb) +{ + struct link_device *ld = &dpld->ld; + struct dpram_boot_frame *bf = (struct dpram_boot_frame *)skb->data; + u8 __iomem *buff = dpld->bt_map.buff; + int err = 0; + + if (bf->len > dpld->bt_map.size) { + mif_info("%s: ERR! Out of DPRAM boundary\n", ld->name); + err = -EINVAL; + goto exit; + } + + if (bf->len) + memcpy(buff, bf->data, bf->len); + + init_completion(&dpld->udl_cmd_complete); + + if (bf->req) + dpld->send_intr(dpld, (u16)bf->req); + + if (bf->resp) { + err = _cbp72_edpram_wait_resp(dpld, bf->resp); + if (err < 0) { + mif_info("%s: ERR! wait_response fail (%d)\n", + ld->name, err); + goto exit; + } else if (err == bf->resp) { + err = skb->len; + } + } + +exit: + dev_kfree_skb_any(skb); + return err; +} + +static int cbp72_download_binary(struct dpram_link_device *dpld, + struct sk_buff *skb) +{ + if (dpld->dp_type == EXT_DPRAM) + return _cbp72_edpram_download_bin(dpld, skb); + else + return -ENODEV; +} + +static int cbp72_dump_start(struct dpram_link_device *dpld) +{ + struct link_device *ld = &dpld->ld; + u8 *dest = dpld->ul_map.buff; + int ret; + + ld->mode = LINK_MODE_ULOAD; + + ret = del_timer(&dpld->dump_timer); + wake_lock(&dpld->wlock); + + iowrite32(DP_MAGIC_UMDL, dpld->ul_map.magic); + + iowrite8((u8)START_FLAG, dest + 0); + iowrite8((u8)0x1, dest + 1); + iowrite8((u8)0x1, dest + 2); + iowrite8((u8)0x0, dest + 3); + iowrite8((u8)END_FLAG, dest + 4); + + init_completion(&dpld->dump_start_complete); + + return 0; +} + +static int _cbp72_edpram_upload(struct dpram_link_device *dpld, + struct dpram_dump_arg *dump, unsigned char __user *target) +{ + struct link_device *ld = &dpld->ld; + struct ul_header header; + u8 *dest = NULL; + u8 *buff = NULL; + u16 plen = 0; + int err = 0; + int ret = 0; + int buff_size = 0; + + mif_debug("\n"); + + init_completion(&dpld->udl_cmd_complete); + + mif_debug("%s: req %x, resp %x", ld->name, dump->req, dump->resp); + + if (dump->req) + dpld->send_intr(dpld, (u16)dump->req); + + if (dump->resp) { + err = _cbp72_edpram_wait_resp(dpld, dump->resp); + if (err < 0) { + mif_info("%s: ERR! wait_response fail (%d)\n", + ld->name, err); + goto exit; + } + } + + if (dump->cmd) + return err; + + dest = (u8 *)dpld->ul_map.buff; + + header.bop = *(u8 *)(dest); + header.total_frame = *(u16 *)(dest + 1); + header.curr_frame = *(u16 *)(dest + 3); + header.len = *(u16 *)(dest + 5); + + mif_debug("%s: total frame:%d, current frame:%d, data len:%d\n", + ld->name, header.total_frame, header.curr_frame, header.len); + + plen = min_t(u16, header.len, DP_DEFAULT_DUMP_LEN); + + buff = vmalloc(DP_DEFAULT_DUMP_LEN); + if (!buff) { + err = -ENOMEM; + goto exit; + } + + memcpy(buff, dest + sizeof(struct ul_header), plen); + ret = copy_to_user(dump->buff, buff, plen); + if (ret < 0) { + mif_info("%s: ERR! dump copy_to_user fail\n", ld->name); + err = -EIO; + goto exit; + } + buff_size = plen; + + ret = copy_to_user(target + 4, &buff_size, sizeof(int)); + if (ret < 0) { + mif_info("%s: ERR! size copy_to_user fail\n", ld->name); + err = -EIO; + goto exit; + } + + vfree(buff); + return err; + +exit: + if (buff) + vfree(buff); + iowrite32(0, dpld->ul_map.magic); + wake_unlock(&dpld->wlock); + return err; +} + +static int cbp72_dump_update(struct dpram_link_device *dpld, void *arg) +{ + struct link_device *ld = &dpld->ld; + struct dpram_dump_arg dump; + int ret; + + ret = copy_from_user(&dump, (void __user *)arg, sizeof(dump)); + if (ret < 0) { + mif_info("%s: ERR! copy_from_user fail\n", ld->name); + return ret; + } + + return _cbp72_edpram_upload(dpld, &dump, (unsigned char __user *)arg); +} + +static int cbp72_set_dl_magic(struct link_device *ld, struct io_device *iod) +{ + struct dpram_link_device *dpld = to_dpram_link_device(ld); + + ld->mode = LINK_MODE_DLOAD; + + iowrite32(DP_MAGIC_DMDL, dpld->dl_map.magic); + + return 0; +} + +static int cbp72_ioctl(struct dpram_link_device *dpld, struct io_device *iod, + unsigned int cmd, unsigned long arg) +{ + struct link_device *ld = &dpld->ld; + int err = 0; + + switch (cmd) { + case IOCTL_MODEM_DL_START: + err = cbp72_set_dl_magic(ld, iod); + if (err < 0) + mif_err("%s: ERR! set_dl_magic fail\n", ld->name); + break; + + default: + mif_err("%s: ERR! invalid cmd 0x%08X\n", ld->name, cmd); + err = -EINVAL; + break; + } + + return err; +} +#endif + +#if defined(CONFIG_LTE_MODEM_CMC221) +static void cmc221_init_boot_map(struct dpram_link_device *dpld) +{ + struct dpram_boot_map *bt_map = &dpld->bt_map; + + bt_map->buff = dpld->dp_base; + bt_map->size = dpld->dp_size; + bt_map->req = (u32 *)(dpld->dp_base + DP_BOOT_REQ_OFFSET); + bt_map->resp = (u32 *)(dpld->dp_base + DP_BOOT_RESP_OFFSET); +} + +static void cmc221_init_dl_map(struct dpram_link_device *dpld) +{ + dpld->dl_map.magic = (u32 *)dpld->dp_base; + dpld->dl_map.buff = (u8 *)dpld->dp_base; +} + +static void cmc221_init_ul_map(struct dpram_link_device *dpld) +{ + dpld->ul_map.magic = (u32 *)dpld->dp_base; + dpld->ul_map.buff = (u8 *)dpld->dp_base; +} + +static int _cmc221_idpram_wait_resp(struct dpram_link_device *dpld, u32 resp) +{ + struct link_device *ld = &dpld->ld; + int count = 50000; + u32 rcvd = 0; + + if (resp == CMC22x_CP_REQ_NV_DATA) { + while (1) { + rcvd = ioread32(dpld->bt_map.resp); + if (rcvd == resp) + break; + + rcvd = dpld->dpctl->recv_msg(); + if (rcvd == 0x9999) { + mif_info("%s: Invalid resp 0x%04X\n", + ld->name, rcvd); + panic("CP Crash ... BAD CRC in CP"); + } + + if (count-- < 0) { + mif_info("%s: Invalid resp 0x%08X\n", + ld->name, rcvd); + return -EAGAIN; + } + + udelay(100); + } + } else { + while (1) { + rcvd = dpld->dpctl->recv_msg(); + + if (rcvd == resp) + break; + + if (resp == CMC22x_CP_RECV_NV_END && + rcvd == CMC22x_CP_CAL_BAD) { + mif_info("%s: CMC22x_CP_CAL_BAD\n", ld->name); + break; + } + + if (count-- < 0) { + mif_info("%s: Invalid resp 0x%04X\n", + ld->name, rcvd); + return -EAGAIN; + } + + udelay(100); + } + } + + return rcvd; +} + +static int _cmc221_idpram_send_boot(struct dpram_link_device *dpld, void *arg) +{ + struct link_device *ld = &dpld->ld; + u8 __iomem *bt_buff = dpld->bt_map.buff; + struct dpram_boot_img cp_img; + u8 *img_buff = NULL; + int err = 0; + int cnt = 0; + + ld->mode = LINK_MODE_BOOT; + + dpld->dpctl->setup_speed(DPRAM_SPEED_LOW); + + /* Test memory... After testing, memory is cleared. */ + if (mif_test_dpram(ld->name, bt_buff, dpld->bt_map.size) < 0) { + mif_info("%s: ERR! mif_test_dpram fail!\n", ld->name); + ld->mode = LINK_MODE_OFFLINE; + return -EIO; + } + + memset(&cp_img, 0, sizeof(struct dpram_boot_img)); + + /* Get information about the boot image */ + err = copy_from_user(&cp_img, arg, sizeof(cp_img)); + mif_info("%s: CP image addr = 0x%08X, size = %d\n", + ld->name, (int)cp_img.addr, cp_img.size); + + /* Alloc a buffer for the boot image */ + img_buff = kzalloc(dpld->bt_map.size, GFP_KERNEL); + if (!img_buff) { + mif_info("%s: ERR! kzalloc fail\n", ld->name); + ld->mode = LINK_MODE_OFFLINE; + return -ENOMEM; + } + + /* Copy boot image from the user space to the image buffer */ + err = copy_from_user(img_buff, cp_img.addr, cp_img.size); + + /* Copy boot image to DPRAM and verify it */ + memcpy(bt_buff, img_buff, cp_img.size); + if (memcmp16_to_io(bt_buff, img_buff, cp_img.size)) { + mif_info("%s: ERR! Boot may be broken!!!\n", ld->name); + goto err; + } + + dpld->dpctl->reset(); + usleep_range(1000, 2000); + + if (cp_img.mode == HOST_BOOT_MODE_NORMAL) { + mif_info("%s: HOST_BOOT_MODE_NORMAL\n", ld->name); + mif_info("%s: Send req 0x%08X\n", ld->name, cp_img.req); + iowrite32(cp_img.req, dpld->bt_map.req); + + /* Wait for cp_img.resp for up to 2 seconds */ + mif_info("%s: Wait resp 0x%08X\n", ld->name, cp_img.resp); + while (ioread32(dpld->bt_map.resp) != cp_img.resp) { + cnt++; + usleep_range(1000, 2000); + + if (cnt > 1000) { + mif_info("%s: ERR! Invalid resp 0x%08X\n", + ld->name, ioread32(dpld->bt_map.resp)); + goto err; + } + } + } else { + mif_info("%s: HOST_BOOT_MODE_DUMP\n", ld->name); + } + + kfree(img_buff); + + mif_info("%s: Send BOOT done\n", ld->name); + + dpld->dpctl->setup_speed(DPRAM_SPEED_HIGH); + + return 0; + +err: + ld->mode = LINK_MODE_OFFLINE; + kfree(img_buff); + + mif_info("%s: ERR! Boot send fail!!!\n", ld->name); + return -EIO; +} + +static int cmc221_download_boot(struct dpram_link_device *dpld, void *arg) +{ + if (dpld->dp_type == CP_IDPRAM) + return _cmc221_idpram_send_boot(dpld, arg); + else + return -ENODEV; +} + +static int _cmc221_idpram_download_bin(struct dpram_link_device *dpld, + struct sk_buff *skb) +{ + int err = 0; + int ret = 0; + struct link_device *ld = &dpld->ld; + struct dpram_boot_frame *bf = (struct dpram_boot_frame *)skb->data; + u8 __iomem *buff = (dpld->bt_map.buff + bf->offset); + + if ((bf->offset + bf->len) > dpld->bt_map.size) { + mif_info("%s: ERR! Out of DPRAM boundary\n", ld->name); + err = -EINVAL; + goto exit; + } + + if (bf->len) + memcpy(buff, bf->data, bf->len); + + if (bf->req) + dpld->dpctl->send_msg((u16)bf->req); + + if (bf->resp) { + err = _cmc221_idpram_wait_resp(dpld, bf->resp); + if (err < 0) + mif_info("%s: ERR! wait_response fail (err %d)\n", + ld->name, err); + } + + if (bf->req == CMC22x_CAL_NV_DOWN_END) + mif_info("%s: CMC22x_CAL_NV_DOWN_END\n", ld->name); + +exit: + if (err < 0) + ret = err; + else + ret = skb->len; + + dev_kfree_skb_any(skb); + + return ret; +} + +static int cmc221_download_binary(struct dpram_link_device *dpld, + struct sk_buff *skb) +{ + if (dpld->dp_type == CP_IDPRAM) + return _cmc221_idpram_download_bin(dpld, skb); + else + return -ENODEV; +} + +static int cmc221_dump_start(struct dpram_link_device *dpld) +{ + struct link_device *ld = &dpld->ld; + int ret; + + ld->mode = LINK_MODE_ULOAD; + + ret = del_timer(&dpld->dump_timer); + wake_lock(&dpld->wlock); + + dpld->dump_rcvd = 0; + iowrite32(CMC22x_CP_DUMP_MAGIC, dpld->ul_map.magic); + + init_completion(&dpld->dump_start_complete); + + return 0; +} + +static void _cmc221_idpram_wait_dump(unsigned long arg) +{ + struct dpram_link_device *dpld = (struct dpram_link_device *)arg; + u16 msg; + + msg = dpld->dpctl->recv_msg(); + if (msg == CMC22x_CP_DUMP_END) { + complete_all(&dpld->dump_recv_done); + return; + } + + if (((dpld->dump_rcvd & 0x1) == 0) && (msg == CMC22x_1ST_BUFF_FULL)) { + complete_all(&dpld->dump_recv_done); + return; + } + + if (((dpld->dump_rcvd & 0x1) == 1) && (msg == CMC22x_2ND_BUFF_FULL)) { + complete_all(&dpld->dump_recv_done); + return; + } + + mif_add_timer(&dpld->dump_timer, DUMP_WAIT_TIMEOUT, + _cmc221_idpram_wait_dump, (unsigned long)dpld); +} + +static int _cmc221_idpram_upload(struct dpram_link_device *dpld, + struct dpram_dump_arg *dumparg) +{ + struct link_device *ld = &dpld->ld; + int ret; + u8 __iomem *src; + int buff_size = CMC22x_DUMP_BUFF_SIZE; + + if ((dpld->dump_rcvd & 0x1) == 0) + dpld->dpctl->send_msg(CMC22x_1ST_BUFF_READY); + else + dpld->dpctl->send_msg(CMC22x_2ND_BUFF_READY); + + init_completion(&dpld->dump_recv_done); + + mif_add_timer(&dpld->dump_timer, DUMP_WAIT_TIMEOUT, + _cmc221_idpram_wait_dump, (unsigned long)dpld); + + ret = wait_for_completion_interruptible_timeout( + &dpld->dump_recv_done, DUMP_TIMEOUT); + if (!ret) { + mif_info("%s: ERR! CP didn't send dump data!!!\n", ld->name); + goto err_out; + } + + if (dpld->dpctl->recv_msg() == CMC22x_CP_DUMP_END) { + mif_info("%s: CMC22x_CP_DUMP_END\n", ld->name); + return 0; + } + + if ((dpld->dump_rcvd & 0x1) == 0) + src = dpld->ul_map.buff; + else + src = dpld->ul_map.buff + CMC22x_DUMP_BUFF_SIZE; + + memcpy(dpld->buff, src, buff_size); + + ret = copy_to_user(dumparg->buff, dpld->buff, buff_size); + if (ret < 0) { + mif_info("%s: ERR! copy_to_user fail\n", ld->name); + goto err_out; + } + + dpld->dump_rcvd++; + return buff_size; + +err_out: + return -EIO; +} + +static int cmc221_dump_update(struct dpram_link_device *dpld, void *arg) +{ + struct link_device *ld = &dpld->ld; + struct dpram_dump_arg dump; + int ret; + + ret = copy_from_user(&dump, (void __user *)arg, sizeof(dump)); + if (ret < 0) { + mif_info("%s: ERR! copy_from_user fail\n", ld->name); + return ret; + } + + return _cmc221_idpram_upload(dpld, &dump); +} + +static int cmc221_ioctl(struct dpram_link_device *dpld, struct io_device *iod, + unsigned int cmd, unsigned long arg) +{ + struct link_device *ld = &dpld->ld; + int err = 0; + + switch (cmd) { + case IOCTL_DPRAM_SEND_BOOT: + err = cmc221_download_boot(dpld, (void *)arg); + if (err < 0) + mif_info("%s: ERR! download_boot fail\n", ld->name); + break; + + default: + mif_err("%s: ERR! invalid cmd 0x%08X\n", ld->name, cmd); + err = -EINVAL; + break; + } + + return err; +} +#endif + +#if defined(CONFIG_CDMA_MODEM_MDM6600) || defined(CONFIG_GSM_MODEM_ESC6270) +enum qc_dload_tag { + QC_DLOAD_TAG_NONE = 0, + QC_DLOAD_TAG_BIN, + QC_DLOAD_TAG_NV, + QC_DLOAD_TAG_MAX +}; + +static void qc_dload_task(unsigned long data); + +static void qc_init_boot_map(struct dpram_link_device *dpld) +{ + struct qc_dpram_boot_map *bt_map = &dpld->qc_bt_map; + struct modemlink_dpram_control *dpctl = dpld->dpctl; + + bt_map->buff = dpld->dp_base; + bt_map->frame_size = (u16 *)(dpld->dp_base + dpctl->boot_size_offset); + bt_map->tag = (u16 *)(dpld->dp_base + dpctl->boot_tag_offset); + bt_map->count = (u16 *)(dpld->dp_base + dpctl->boot_count_offset); + + tasklet_init(&dpld->dl_tsk, qc_dload_task, (unsigned long)dpld); +} + +static int qc_prepare_download(struct dpram_link_device *dpld) +{ + int retval = 0; + int count = 0; + + while (1) { + if (dpld->udl_check.copy_start) { + dpld->udl_check.copy_start = 0; + break; + } + + msleep_interruptible(10); + + count++; + if (count > 200) { + mif_err("ERR! count %d\n", count); + return -1; + } + } + + return retval; +} + +static void _qc_do_download(struct dpram_link_device *dpld, + struct dpram_udl_param *param) +{ + struct qc_dpram_boot_map *bt_map = &dpld->qc_bt_map; + + if (param->size <= dpld->dpctl->max_boot_frame_size) { + memcpy(bt_map->buff, param->addr, param->size); + iowrite16(param->size, bt_map->frame_size); + iowrite16(param->tag, bt_map->tag); + iowrite16(param->count, bt_map->count); + dpld->send_intr(dpld, 0xDB12); + } else { + mif_info("param->size %d\n", param->size); + } +} + +static int _qc_download(struct dpram_link_device *dpld, void *arg, + enum qc_dload_tag tag) +{ + int retval = 0; + int count = 0; + int cnt_limit; + unsigned char *img; + struct dpram_udl_param param; + + retval = copy_from_user((void *)¶m, (void *)arg, sizeof(param)); + if (retval < 0) { + mif_err("ERR! copy_from_user fail\n"); + return -1; + } + + img = vmalloc(param.size); + if (!img) { + mif_err("ERR! vmalloc fail\n"); + return -1; + } + memset(img, 0, param.size); + memcpy(img, param.addr, param.size); + + dpld->udl_check.total_size = param.size; + dpld->udl_check.rest_size = param.size; + dpld->udl_check.send_size = 0; + dpld->udl_check.copy_complete = 0; + + dpld->udl_param.addr = img; + dpld->udl_param.size = dpld->dpctl->max_boot_frame_size; + if (tag == QC_DLOAD_TAG_NV) + dpld->udl_param.count = 1; + else + dpld->udl_param.count = param.count; + dpld->udl_param.tag = tag; + + if (dpld->udl_check.rest_size < dpld->dpctl->max_boot_frame_size) + dpld->udl_param.size = dpld->udl_check.rest_size; + + /* Download image (binary or NV) */ + _qc_do_download(dpld, &dpld->udl_param); + + /* Wait for completion + */ + if (tag == QC_DLOAD_TAG_NV) + cnt_limit = 200; + else + cnt_limit = 1000; + + while (1) { + if (dpld->udl_check.copy_complete) { + dpld->udl_check.copy_complete = 0; + retval = 0; + break; + } + + msleep(10); + + count++; + if (count > cnt_limit) { + dpld->udl_check.total_size = 0; + dpld->udl_check.rest_size = 0; + mif_err("ERR! count %d\n", count); + retval = -1; + break; + } + } + + vfree(img); + + return retval; +} + +static int qc_download_binary(struct dpram_link_device *dpld, void *arg) +{ + return _qc_download(dpld, arg, QC_DLOAD_TAG_BIN); +} + +static int qc_download_nv(struct dpram_link_device *dpld, void *arg) +{ + return _qc_download(dpld, arg, QC_DLOAD_TAG_NV); +} + +static void qc_dload_task(unsigned long data) +{ + struct dpram_link_device *dpld = (struct dpram_link_device *)data; + + dpld->udl_check.send_size += dpld->udl_param.size; + dpld->udl_check.rest_size -= dpld->udl_param.size; + + dpld->udl_param.addr += dpld->udl_param.size; + + if (dpld->udl_check.send_size >= dpld->udl_check.total_size) { + dpld->udl_check.copy_complete = 1; + dpld->udl_param.tag = 0; + return; + } + + if (dpld->udl_check.rest_size < dpld->dpctl->max_boot_frame_size) + dpld->udl_param.size = dpld->udl_check.rest_size; + + dpld->udl_param.count += 1; + + _qc_do_download(dpld, &dpld->udl_param); +} + +static void qc_dload_cmd_handler(struct dpram_link_device *dpld, u16 cmd) +{ + switch (cmd) { + case 0x1234: + dpld->udl_check.copy_start = 1; + break; + + case 0xDBAB: + if (dpld->udl_check.total_size) + tasklet_schedule(&dpld->dl_tsk); + break; + + case 0xABCD: + dpld->udl_check.boot_complete = 1; + break; + + default: + mif_err("ERR! unknown command 0x%04X\n", cmd); + } +} + +static int qc_boot_start(struct dpram_link_device *dpld) +{ + u16 mask = 0; + int count = 0; + + /* Send interrupt -> '0x4567' */ + mask = 0x4567; + dpld->send_intr(dpld, mask); + + while (1) { + if (dpld->udl_check.boot_complete) { + dpld->udl_check.boot_complete = 0; + break; + } + + msleep_interruptible(10); + + count++; + if (count > 200) { + mif_err("ERR! count %d\n", count); + return -1; + } + } + + return 0; +} + +static int qc_boot_post_process(struct dpram_link_device *dpld) +{ + int count = 0; + + while (1) { + if (dpld->boot_start_complete) { + dpld->boot_start_complete = 0; + break; + } + + msleep_interruptible(10); + + count++; + if (count > 200) { + mif_err("ERR! count %d\n", count); + return -1; + } + } + + return 0; +} + +static void qc_start_handler(struct dpram_link_device *dpld) +{ + /* + * INT_MASK_VALID | INT_MASK_CMD | INT_MASK_CP_AIRPLANE_BOOT | + * INT_MASK_CP_AP_ANDROID | INT_MASK_CMD_INIT_END + */ + u16 mask = (0x0080 | 0x0040 | 0x1000 | 0x0100 | 0x0002); + + dpld->boot_start_complete = 1; + + /* Send INIT_END code to CP */ + mif_info("send 0x%04X (INIT_END)\n", mask); + + dpld->send_intr(dpld, mask); +} + +static void qc_crash_log(struct dpram_link_device *dpld) +{ + struct link_device *ld = &dpld->ld; + static unsigned char buf[151]; + u8 __iomem *data = NULL; + + data = dpld->get_rx_buff(dpld, IPC_FMT); + memcpy(buf, data, (sizeof(buf) - 1)); + + mif_info("PHONE ERR MSG\t| %s Crash\n", ld->mdm_data->name); + mif_info("PHONE ERR MSG\t| %s\n", buf); +} + +static int _qc_data_upload(struct dpram_link_device *dpld, + struct dpram_udl_param *param) +{ + struct qc_dpram_boot_map *bt_map = &dpld->qc_bt_map; + int retval = 0; + u16 intval = 0; + int count = 0; + + while (1) { + if (!gpio_get_value(dpld->gpio_dpram_int)) { + intval = dpld->recv_intr(dpld); + if (intval == 0xDBAB) { + break; + } else { + mif_err("intr 0x%08x\n", intval); + return -1; + } + } + + msleep_interruptible(1); + + count++; + if (count > 200) { + mif_err("<%s:%d>\n", __func__, __LINE__); + return -1; + } + } + + param->size = ioread16(bt_map->frame_size); + memcpy(param->addr, bt_map->buff, param->size); + param->tag = ioread16(bt_map->tag); + param->count = ioread16(bt_map->count); + + dpld->send_intr(dpld, 0xDB12); + + return retval; +} + +static int qc_uload_step1(struct dpram_link_device *dpld) +{ + int retval = 0; + int count = 0; + u16 intval = 0; + u16 mask = 0; + + mif_info("+---------------------------------------------+\n"); + mif_info("| UPLOAD PHONE SDRAM |\n"); + mif_info("+---------------------------------------------+\n"); + + while (1) { + if (!gpio_get_value(dpld->gpio_dpram_int)) { + intval = dpld->recv_intr(dpld); + mif_info("intr 0x%04x\n", intval); + if (intval == 0x1234) { + break; + } else { + mif_info("ERR! invalid intr\n"); + return -1; + } + } + + msleep_interruptible(1); + + count++; + if (count > 200) { + intval = dpld->recv_intr(dpld); + mif_info("count %d, intr 0x%04x\n", count, intval); + if (intval == 0x1234) + break; + return -1; + } + } + + mask = 0xDEAD; + dpld->send_intr(dpld, mask); + + return retval; +} + +static int qc_uload_step2(struct dpram_link_device *dpld, void *arg) +{ + int retval = 0; + struct dpram_udl_param param; + + retval = copy_from_user((void *)¶m, (void *)arg, sizeof(param)); + if (retval < 0) { + mif_err("ERR! copy_from_user fail (err %d)\n", retval); + return -1; + } + + retval = _qc_data_upload(dpld, ¶m); + if (retval < 0) { + mif_err("ERR! _qc_data_upload fail (err %d)\n", retval); + return -1; + } + + if (!(param.count % 500)) + mif_info("param->count = %d\n", param.count); + + if (param.tag == 4) { + enable_irq(dpld->irq); + mif_info("param->tag = %d\n", param.tag); + } + + retval = copy_to_user((unsigned long *)arg, ¶m, sizeof(param)); + if (retval < 0) { + mif_err("ERR! copy_to_user fail (err %d)\n", retval); + return -1; + } + + return retval; +} + +static int qc_ioctl(struct dpram_link_device *dpld, struct io_device *iod, + unsigned int cmd, unsigned long arg) +{ + struct link_device *ld = &dpld->ld; + int err = 0; + + switch (cmd) { + case IOCTL_DPRAM_PHONE_POWON: + err = qc_prepare_download(dpld); + if (err < 0) + mif_info("%s: ERR! prepare_download fail\n", ld->name); + break; + + case IOCTL_DPRAM_PHONEIMG_LOAD: + err = qc_download_binary(dpld, (void *)arg); + if (err < 0) + mif_info("%s: ERR! download_binary fail\n", ld->name); + break; + + case IOCTL_DPRAM_NVDATA_LOAD: + err = qc_download_nv(dpld, (void *)arg); + if (err < 0) + mif_info("%s: ERR! download_nv fail\n", ld->name); + break; + + case IOCTL_DPRAM_PHONE_BOOTSTART: + err = qc_boot_start(dpld); + if (err < 0) { + mif_info("%s: ERR! boot_start fail\n", ld->name); + break; + } + + err = qc_boot_post_process(dpld); + if (err < 0) + mif_info("%s: ERR! boot_post_process fail\n", ld->name); + + break; + + case IOCTL_DPRAM_PHONE_UPLOAD_STEP1: + disable_irq_nosync(dpld->irq); + err = qc_uload_step1(dpld); + if (err < 0) { + enable_irq(dpld->irq); + mif_info("%s: ERR! upload_step1 fail\n", ld->name); + } + break; + + case IOCTL_DPRAM_PHONE_UPLOAD_STEP2: + err = qc_uload_step2(dpld, (void *)arg); + if (err < 0) { + enable_irq(dpld->irq); + mif_info("%s: ERR! upload_step2 fail\n", ld->name); + } + break; + + default: + mif_err("%s: ERR! invalid cmd 0x%08X\n", ld->name, cmd); + err = -EINVAL; + break; + } + + return err; +} + +static irqreturn_t qc_dpram_irq_handler(int irq, void *data) +{ + struct dpram_link_device *dpld = (struct dpram_link_device *)data; + struct link_device *ld = (struct link_device *)&dpld->ld; + u16 int2ap = 0; + + if (unlikely(ld->mode == LINK_MODE_OFFLINE)) + return IRQ_HANDLED; + + int2ap = dpld->recv_intr(dpld); + + if (int2ap == INT_POWERSAFE_FAIL) { + mif_info("%s: int2ap == INT_POWERSAFE_FAIL\n", ld->name); + goto exit; + } + + if (int2ap == 0x1234 || int2ap == 0xDBAB || int2ap == 0xABCD) { + qc_dload_cmd_handler(dpld, int2ap); + goto exit; + } + + if (likely(INT_VALID(int2ap))) + dpld->ipc_rx_handler(dpld, int2ap); + else + mif_info("%s: ERR! invalid intr 0x%04X\n", ld->name, int2ap); + +exit: + return IRQ_HANDLED; +} +#endif + +static struct dpram_ext_op ext_op_set[] = { +#ifdef CONFIG_CDMA_MODEM_CBP72 + [VIA_CBP72] = { + .exist = 1, + .init_boot_map = cbp72_init_boot_map, + .init_dl_map = cbp72_init_dl_map, + .download_binary = cbp72_download_binary, + .dump_start = cbp72_dump_start, + .dump_update = cbp72_dump_update, + .ioctl = cbp72_ioctl, + }, +#endif +#ifdef CONFIG_LTE_MODEM_CMC221 + [SEC_CMC221] = { + .exist = 1, + .init_boot_map = cmc221_init_boot_map, + .init_dl_map = cmc221_init_dl_map, + .init_ul_map = cmc221_init_ul_map, + .download_binary = cmc221_download_binary, + .dump_start = cmc221_dump_start, + .dump_update = cmc221_dump_update, + .ioctl = cmc221_ioctl, + }, +#endif +#if defined(CONFIG_CDMA_MODEM_MDM6600) + [QC_MDM6600] = { + .exist = 1, + .init_boot_map = qc_init_boot_map, + .cp_start_handler = qc_start_handler, + .crash_log = qc_crash_log, + .ioctl = qc_ioctl, + .irq_handler = qc_dpram_irq_handler, + }, +#endif +#if defined(CONFIG_GSM_MODEM_ESC6270) + [QC_ESC6270] = { + .exist = 1, + .init_boot_map = qc_init_boot_map, + .cp_start_handler = qc_start_handler, + .crash_log = qc_crash_log, + .ioctl = qc_ioctl, + .irq_handler = qc_dpram_irq_handler, + }, +#endif +}; + +struct dpram_ext_op *dpram_get_ext_op(enum modem_t modem) +{ + if (ext_op_set[modem].exist) + return &ext_op_set[modem]; + else + return NULL; +} + diff --git a/drivers/misc/modem_if/modem_link_device_hsic.c b/drivers/misc/modem_if/modem_link_device_hsic.c index af058f4..955c620 100755..100644 --- a/drivers/misc/modem_if/modem_link_device_hsic.c +++ b/drivers/misc/modem_if/modem_link_device_hsic.c @@ -47,18 +47,6 @@ static int usb_tx_urb_with_skb(struct usb_device *usbdev, struct sk_buff *skb, #endif static void usb_rx_complete(struct urb *urb); -#if 1 -static void usb_set_autosuspend_delay(struct usb_device *usbdev, int delay) -{ - pm_runtime_set_autosuspend_delay(&usbdev->dev, delay); -} -#else -static void usb_set_autosuspend_delay(struct usb_device *usbdev, int delay) -{ - usbdev->autosuspend_delay = msecs_to_jiffies(delay); -} -#endif - static int start_ipc(struct link_device *ld, struct io_device *iod) { struct sk_buff *skb; @@ -177,7 +165,7 @@ static int usb_rx_submit(struct usb_link_device *usb_ld, pipe_data->rx_buf_size, usb_rx_complete, (void *)pipe_data); - if (!usb_ld->if_usb_connected || !usb_ld->usbdev) + if (pipe_data->disconnected) return -ENOENT; usb_mark_last_busy(usb_ld->usbdev); @@ -406,6 +394,8 @@ static void usb_tx_complete(struct urb *urb) { struct sk_buff *skb = urb->context; struct io_device *iod = skbpriv(skb)->iod; + struct link_device *ld = skbpriv(skb)->ld; + struct usb_link_device *usb_ld = to_usb_link_device(ld); switch (urb->status) { case 0: @@ -419,7 +409,7 @@ static void usb_tx_complete(struct urb *urb) } dev_kfree_skb_any(skb); - if (urb->dev) + if (urb->dev && usb_ld->if_usb_connected) usb_mark_last_busy(urb->dev); usb_free_urb(urb); } @@ -439,14 +429,7 @@ static int usb_tx_urb_with_skb(struct usb_device *usbdev, struct sk_buff *skb, mif_err("alloc urb error\n"); return -ENOMEM; } -#if 0 - int i; - for (i = 0; i < skb->len; i++) { - if (i > 16) - break; - mif_err("[0x%02x]", *(skb->data + i)); - } -#endif + urb->transfer_flags = URB_ZERO_PACKET; usb_fill_bulk_urb(urb, pipe_data->usbdev, pipe_data->tx_pipe, skb->data, skb->len, usb_tx_complete, (void *)skb); @@ -537,7 +520,7 @@ static void usb_tx_work(struct work_struct *work) * probing, _usb_tx_work return to -ENOENT then runtime usage * count allways positive and never enter to L2 */ - if (!usb_ld->if_usb_connected_last) { + if (!usb_ld->if_usb_connected) { mif_info("link is available, but if was not readey\n"); goto retry_tx_work; } @@ -649,7 +632,7 @@ static void link_pm_runtime_start(struct work_struct *work) struct link_pm_data *pm_data = container_of(work, struct link_pm_data, link_pm_start.work); struct usb_device *usbdev = pm_data->usb_ld->usbdev; - struct device *dev, *ppdev; + struct device *dev, *hdev; struct link_device *ld = &pm_data->usb_ld->ld; if (!pm_data->usb_ld->if_usb_connected @@ -671,13 +654,15 @@ static void link_pm_runtime_start(struct work_struct *work) if (pm_data->usb_ld->usbdev && dev->parent) { mif_info("rpm_status: %d\n", dev->power.runtime_status); - usb_set_autosuspend_delay(usbdev, 200); - ppdev = dev->parent->parent; + pm_runtime_set_autosuspend_delay(dev, 200); + hdev = usbdev->bus->root_hub->dev.parent; + mif_info("EHCI runtime %s, %s\n", dev_driver_string(hdev), + dev_name(hdev)); pm_runtime_allow(dev); - pm_runtime_allow(ppdev);/*ehci*/ + pm_runtime_allow(hdev);/*ehci*/ pm_data->link_pm_active = true; pm_data->resume_requested = false; - pm_data->link_reconnect_cnt = 2; + pm_data->link_reconnect_cnt = 5; pm_data->resume_retry_cnt = 0; /* retry prvious link tx q */ @@ -768,12 +753,6 @@ static inline int link_pm_slave_wake(struct link_pm_data *pm_data) HOSTWAKE_TRIGLEVEL) mdelay(5); } - /* runtime pm goes to active */ - if (!gpio_get_value(pm_data->gpio_link_active)) { - mif_debug("gpio [H ACTV : %d] set 1\n", - gpio_get_value(pm_data->gpio_link_active)); - gpio_set_value(pm_data->gpio_link_active, 1); - } return spin; } @@ -833,6 +812,11 @@ static void link_pm_runtime_work(struct work_struct *work) } wake_unlock(&pm_data->rpm_wake); break; + case RPM_SUSPENDING: + /* Checking the usb_runtime_suspend running time.*/ + mif_info("rpm_states=%d", dev->power.runtime_status); + msleep(20); + break; default: break; } @@ -919,6 +903,7 @@ static long link_pm_ioctl(struct file *file, unsigned int cmd, { int value; struct link_pm_data *pm_data = file->private_data; + struct modem_ctl *mc = if_usb_get_modemctl(pm_data); mif_info("%x\n", cmd); @@ -955,6 +940,8 @@ static long link_pm_ioctl(struct file *file, unsigned int cmd, irq_set_irq_type(pm_data->irq_link_hostwake, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING); } + case IOCTL_LINK_GET_PHONEACTIVE: + return gpio_get_value(mc->gpio_phone_active); default: break; } @@ -1001,7 +988,10 @@ static int link_pm_notifier_event(struct notifier_block *this, pm_data->dpm_suspending = true; #ifdef CONFIG_UMTS_MODEM_XMM6262 /* set PDA Active High if previous state was LPA */ - gpio_set_value(mc->gpio_pda_active, 1); + if (!gpio_get_value(pm_data->gpio_link_active)) { + mif_info("PDA active High to LPA suspend spot\n"); + gpio_set_value(mc->gpio_pda_active, 1); + } #endif mif_debug("dpm suspending set to true\n"); return NOTIFY_OK; @@ -1017,6 +1007,14 @@ static int link_pm_notifier_event(struct notifier_block *this, 0); mif_info("post resume\n"); } +#ifdef CONFIG_UMTS_MODEM_XMM6262 + /* LPA to Kernel suspend and User Freezing task fail resume, + restore to LPA GPIO states. */ + if (!gpio_get_value(pm_data->gpio_link_active)) { + mif_info("PDA active low to LPA GPIO state\n"); + gpio_set_value(mc->gpio_pda_active, 0); + } +#endif mif_debug("dpm suspending set to false\n"); return NOTIFY_OK; } @@ -1122,7 +1120,7 @@ static void if_usb_disconnect(struct usb_interface *intf) { struct if_usb_devdata *devdata = usb_get_intfdata(intf); struct link_pm_data *pm_data = devdata->usb_ld->link_pm_data; - struct device *dev, *ppdev; + struct device *dev, *hdev; struct link_device *ld = &devdata->usb_ld->ld; mif_info("\n"); @@ -1130,13 +1128,14 @@ static void if_usb_disconnect(struct usb_interface *intf) if (devdata->disconnected) return; + devdata->usb_ld->if_usb_connected = 0; + usb_driver_release_interface(to_usb_driver(intf->dev.driver), intf); usb_kill_urb(devdata->urb); - dev = &devdata->usb_ld->usbdev->dev; - ppdev = dev->parent->parent; - pm_runtime_forbid(ppdev); /*ehci*/ + hdev = devdata->usbdev->bus->root_hub->dev.parent; + pm_runtime_forbid(hdev); /*ehci*/ mif_info("put dev 0x%p\n", devdata->usbdev); usb_put_dev(devdata->usbdev); @@ -1148,8 +1147,6 @@ static void if_usb_disconnect(struct usb_interface *intf) devdata->state = STATE_SUSPENDED; pm_data->ipc_debug_cnt = 0; - devdata->usb_ld->if_usb_connected = 0; - devdata->usb_ld->if_usb_connected_last = 0; devdata->usb_ld->suspended = 0; wake_lock(&pm_data->boot_wake); @@ -1170,9 +1167,12 @@ static void if_usb_disconnect(struct usb_interface *intf) if (devdata->usb_ld->ld.com_state != COM_ONLINE) { cancel_delayed_work(&pm_data->link_reconnect_work); return; - } else + } else { + if (pm_data->ehci_reg_dump) + pm_data->ehci_reg_dump(hdev); schedule_delayed_work(&pm_data->link_reconnect_work, msecs_to_jiffies(500)); + } return; } @@ -1206,9 +1206,6 @@ static int if_usb_set_pipe(struct usb_link_device *usb_ld, return 0; } - -static struct usb_id_info hsic_channel_info; - static int __devinit if_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) { @@ -1226,12 +1223,25 @@ static int __devinit if_usb_probe(struct usb_interface *intf, mif_info("usbdev = 0x%p\n", usbdev); + pr_debug("%s: Class=%d, SubClass=%d, Protocol=%d\n", __func__, + intf->altsetting->desc.bInterfaceClass, + intf->altsetting->desc.bInterfaceSubClass, + intf->altsetting->desc.bInterfaceProtocol); + + /* if usb disconnected, AP try to reconnect 5 times. + * but because if_sub_connected is configured + * at the end of if_usb_probe, there was a chance + * that swk will be called again during enumeration. + * so.. cancel reconnect work_queue in this case. */ + if (usb_ld->ld.com_state == COM_ONLINE) + cancel_delayed_work(&usb_ld->link_pm_data->link_reconnect_work); + usb_ld->usbdev = usbdev; pm_runtime_forbid(&usbdev->dev); usb_ld->link_pm_data->link_pm_active = false; usb_ld->link_pm_data->dpm_suspending = false; usb_ld->link_pm_data->ipc_debug_cnt = 0; - usb_ld->if_usb_is_main = (info == &hsic_channel_info); + usb_ld->if_usb_is_main = (info->intf_id != BOOT_DOWN); union_hdr = NULL; /* for WMC-ACM compatibility, WMC-ACM use an end-point for control msg*/ @@ -1314,7 +1324,6 @@ static int __devinit if_usb_probe(struct usb_interface *intf, usb_ld->devdata[pipe].disconnected = 0; usb_ld->devdata[pipe].state = STATE_RESUMED; - usb_ld->if_usb_connected = 1; usb_ld->suspended = 0; err = usb_driver_claim_interface(usbdrv, data_intf, @@ -1345,7 +1354,7 @@ static int __devinit if_usb_probe(struct usb_interface *intf, link_pm_change_modem_state(usb_ld->link_pm_data, STATE_ONLINE); if (pipe == IF_USB_CMD_EP || info->intf_id == BOOT_DOWN) - usb_ld->if_usb_connected_last = 1; + usb_ld->if_usb_connected = 1; mif_info("successfully done\n"); @@ -1369,9 +1378,11 @@ static struct usb_id_info hsic_channel_info = { }; static struct usb_device_id if_usb_ids[] = { - {USB_DEVICE(IMC_BOOT_VID, IMC_BOOT_PID), + {USB_DEVICE_AND_INTERFACE_INFO(IMC_BOOT_VID, IMC_BOOT_PID, + USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, USB_CDC_PROTO_NONE), .driver_info = (unsigned long)&hsic_boot_down_info,}, - {USB_DEVICE(IMC_MAIN_VID, IMC_MAIN_PID), + {USB_DEVICE_AND_INTERFACE_INFO(IMC_MAIN_VID, IMC_MAIN_PID, + USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, 1), .driver_info = (unsigned long)&hsic_channel_info,}, {USB_DEVICE(STE_BOOT_VID, STE_BOOT_PID), .driver_info = (unsigned long)&hsic_boot_down_info,}, @@ -1446,13 +1457,21 @@ static int usb_link_pm_init(struct usb_link_device *usb_ld, void *data) struct platform_device *pdev = (struct platform_device *)data; struct modem_data *pdata = (struct modem_data *)pdev->dev.platform_data; - struct modemlink_pm_data *pm_pdata = pdata->link_pm_data; + struct modemlink_pm_data *pm_pdata; struct link_pm_data *pm_data = kzalloc(sizeof(struct link_pm_data), GFP_KERNEL); + + if (!pdata || !pdata->link_pm_data) { + mif_err("platform data is NULL\n"); + return -EINVAL; + } + pm_pdata = pdata->link_pm_data; + if (!pm_data) { mif_err("link_pm_data is NULL\n"); return -ENOMEM; } + /* get link pm data from modemcontrol's platform data */ pm_data->gpio_link_active = pm_pdata->gpio_link_active; pm_data->gpio_link_enable = pm_pdata->gpio_link_enable; @@ -1461,6 +1480,7 @@ static int usb_link_pm_init(struct usb_link_device *usb_ld, void *data) pm_data->irq_link_hostwake = gpio_to_irq(pm_data->gpio_link_hostwake); pm_data->link_ldo_enable = pm_pdata->link_ldo_enable; pm_data->link_reconnect = pm_pdata->link_reconnect; + pm_data->ehci_reg_dump = pm_pdata->ehci_reg_dump; pm_data->usb_ld = usb_ld; pm_data->link_pm_active = false; diff --git a/drivers/misc/modem_if/modem_link_device_hsic.h b/drivers/misc/modem_if/modem_link_device_hsic.h index 8aec412..604b067 100755..100644 --- a/drivers/misc/modem_if/modem_link_device_hsic.h +++ b/drivers/misc/modem_if/modem_link_device_hsic.h @@ -36,6 +36,7 @@ enum { #define IOCTL_LINK_GET_HOSTWAKE _IO('o', 0x32) #define IOCTL_LINK_CONNECTED _IO('o', 0x33) #define IOCTL_LINK_SET_BIAS_CLEAR _IO('o', 0x34) +#define IOCTL_LINK_GET_PHONEACTIVE _IO('o', 0x35) /* VID,PID for IMC - XMM6260, XMM6262*/ #define IMC_BOOT_VID 0x058b @@ -102,6 +103,8 @@ struct link_pm_data { unsigned ipc_debug_cnt; unsigned long tx_cnt; unsigned long rx_cnt; + + void (*ehci_reg_dump)(struct device *); }; struct if_usb_devdata { @@ -130,10 +133,6 @@ struct usb_link_device { unsigned int suspended; int if_usb_connected; - /*It is same with if_usb_connected, but we need to check the side-effect - * from timming changed, it will merge with if_usb_connect variable.*/ - int if_usb_connected_last; - bool if_usb_is_main; /* boot,down(false) or main(true) */ /* LINK PM DEVICE DATA */ diff --git a/drivers/misc/modem_if/modem_link_device_pld.c b/drivers/misc/modem_if/modem_link_device_pld.c new file mode 100644 index 0000000..b68040e --- /dev/null +++ b/drivers/misc/modem_if/modem_link_device_pld.c @@ -0,0 +1,1981 @@ +/* + * Copyright (C) 2010 Samsung Electronics. + * + * 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/irq.h> +#include <linux/gpio.h> +#include <linux/time.h> +#include <linux/interrupt.h> +#include <linux/timer.h> +#include <linux/wakelock.h> +#include <linux/delay.h> +#include <linux/wait.h> +#include <linux/sched.h> +#include <linux/vmalloc.h> +#include <linux/if_arp.h> +#include <linux/platform_device.h> +#include <linux/kallsyms.h> +#include <linux/platform_data/modem.h> + +#include "modem_prj.h" +#include "modem_link_device_pld.h" +#include "modem_utils.h" + + +/* +** Function prototypes for basic DPRAM operations +*/ +static inline void clear_intr(struct dpram_link_device *dpld); +static inline u16 recv_intr(struct dpram_link_device *dpld); +static inline void send_intr(struct dpram_link_device *dpld, u16 mask); + +static inline u16 get_magic(struct dpram_link_device *dpld); +static inline void set_magic(struct dpram_link_device *dpld, u16 val); +static inline u16 get_access(struct dpram_link_device *dpld); +static inline void set_access(struct dpram_link_device *dpld, u16 val); + +static inline u32 get_tx_head(struct dpram_link_device *dpld, int id); +static inline u32 get_tx_tail(struct dpram_link_device *dpld, int id); +static inline void set_tx_head(struct dpram_link_device *dpld, int id, u32 in); +static inline void set_tx_tail(struct dpram_link_device *dpld, int id, u32 out); +static inline u8 *get_tx_buff(struct dpram_link_device *dpld, int id); +static inline u32 get_tx_buff_size(struct dpram_link_device *dpld, int id); + +static inline u32 get_rx_head(struct dpram_link_device *dpld, int id); +static inline u32 get_rx_tail(struct dpram_link_device *dpld, int id); +static inline void set_rx_head(struct dpram_link_device *dpld, int id, u32 in); +static inline void set_rx_tail(struct dpram_link_device *dpld, int id, u32 out); +static inline u8 *get_rx_buff(struct dpram_link_device *dpld, int id); +static inline u32 get_rx_buff_size(struct dpram_link_device *dpld, int id); + +static inline u16 get_mask_req_ack(struct dpram_link_device *dpld, int id); +static inline u16 get_mask_res_ack(struct dpram_link_device *dpld, int id); +static inline u16 get_mask_send(struct dpram_link_device *dpld, int id); + +static inline bool dpram_circ_valid(u32 size, u32 in, u32 out); + +static void handle_cp_crash(struct dpram_link_device *dpld); +static int trigger_force_cp_crash(struct dpram_link_device *dpld); + +/* +** Functions for debugging +*/ +static inline void log_dpram_status(struct dpram_link_device *dpld) +{ + pr_info("mif: %s: {M:0x%X A:%d} {FMT TI:%u TO:%u RI:%u RO:%u} " + "{RAW TI:%u TO:%u RI:%u RO:%u} {INT:0x%X}\n", + dpld->ld.mc->name, + get_magic(dpld), get_access(dpld), + get_tx_head(dpld, IPC_FMT), get_tx_tail(dpld, IPC_FMT), + get_rx_head(dpld, IPC_FMT), get_rx_tail(dpld, IPC_FMT), + get_tx_head(dpld, IPC_RAW), get_tx_tail(dpld, IPC_RAW), + get_rx_head(dpld, IPC_RAW), get_rx_tail(dpld, IPC_RAW), + recv_intr(dpld)); +} + +static void set_dpram_map(struct dpram_link_device *dpld, + struct mif_irq_map *map) +{ + map->magic = get_magic(dpld); + map->access = get_access(dpld); + + map->fmt_tx_in = get_tx_head(dpld, IPC_FMT); + map->fmt_tx_out = get_tx_tail(dpld, IPC_FMT); + map->fmt_rx_in = get_rx_head(dpld, IPC_FMT); + map->fmt_rx_out = get_rx_tail(dpld, IPC_FMT); + map->raw_tx_in = get_tx_head(dpld, IPC_RAW); + map->raw_tx_out = get_tx_tail(dpld, IPC_RAW); + map->raw_rx_in = get_rx_head(dpld, IPC_RAW); + map->raw_rx_out = get_rx_tail(dpld, IPC_RAW); + + map->cp2ap = recv_intr(dpld); +} + +/* +** RXB (DPRAM RX buffer) functions +*/ +static struct dpram_rxb *rxbq_create_pool(unsigned size, int count) +{ + struct dpram_rxb *rxb; + u8 *buff; + int i; + + rxb = kzalloc(sizeof(struct dpram_rxb) * count, GFP_KERNEL); + if (!rxb) { + mif_info("ERR! kzalloc rxb fail\n"); + return NULL; + } + + buff = kzalloc((size * count), GFP_KERNEL|GFP_DMA); + if (!buff) { + mif_info("ERR! kzalloc buff fail\n"); + kfree(rxb); + return NULL; + } + + for (i = 0; i < count; i++) { + rxb[i].buff = buff; + rxb[i].size = size; + buff += size; + } + + return rxb; +} + +static inline unsigned rxbq_get_page_size(unsigned len) +{ + return ((len + PAGE_SIZE - 1) >> PAGE_SHIFT) << PAGE_SHIFT; +} + +static inline bool rxbq_empty(struct dpram_rxb_queue *rxbq) +{ + return (rxbq->in == rxbq->out) ? true : false; +} + +static inline int rxbq_free_size(struct dpram_rxb_queue *rxbq) +{ + int in = rxbq->in; + int out = rxbq->out; + int qsize = rxbq->size; + return (in < out) ? (out - in - 1) : (qsize + out - in - 1); +} + +static inline struct dpram_rxb *rxbq_get_free_rxb(struct dpram_rxb_queue *rxbq) +{ + struct dpram_rxb *rxb = NULL; + + if (likely(rxbq_free_size(rxbq) > 0)) { + rxb = &rxbq->rxb[rxbq->in]; + rxbq->in++; + if (rxbq->in >= rxbq->size) + rxbq->in -= rxbq->size; + rxb->data = rxb->buff; + } + + return rxb; +} + +static inline int rxbq_size(struct dpram_rxb_queue *rxbq) +{ + int in = rxbq->in; + int out = rxbq->out; + int qsize = rxbq->size; + return (in >= out) ? (in - out) : (qsize - out + in); +} + +static inline struct dpram_rxb *rxbq_get_data_rxb(struct dpram_rxb_queue *rxbq) +{ + struct dpram_rxb *rxb = NULL; + + if (likely(!rxbq_empty(rxbq))) { + rxb = &rxbq->rxb[rxbq->out]; + rxbq->out++; + if (rxbq->out >= rxbq->size) + rxbq->out -= rxbq->size; + } + + return rxb; +} + +static inline u8 *rxb_put(struct dpram_rxb *rxb, unsigned len) +{ + rxb->len = len; + return rxb->data; +} + +static inline void rxb_clear(struct dpram_rxb *rxb) +{ + rxb->data = NULL; + rxb->len = 0; +} + +/* +** DPRAM operations +*/ +static int dpram_register_isr(unsigned irq, irqreturn_t (*isr)(int, void*), + unsigned long flag, const char *name, + struct dpram_link_device *dpld) +{ + int ret = 0; + + ret = request_irq(irq, isr, flag, name, dpld); + if (ret) { + mif_info("%s: ERR! request_irq fail (err %d)\n", name, ret); + return ret; + } + + ret = enable_irq_wake(irq); + if (ret) + mif_info("%s: ERR! enable_irq_wake fail (err %d)\n", name, ret); + + mif_info("%s (#%d) handler registered\n", name, irq); + + return 0; +} + +static inline void clear_intr(struct dpram_link_device *dpld) +{ + if (dpld->dpctl->clear_intr) + dpld->dpctl->clear_intr(); +} + +static inline u16 recv_intr(struct dpram_link_device *dpld) +{ + u16 val1 = 0, val2 = 0, cnt = 3; + unsigned long int flags; + + spin_lock_irqsave(&dpld->pld_lock, flags); + + do { + /* Check head value written */ + iowrite16(PLD_ADDR_MASK(&dpld->mbx2ap[0]), + dpld->address_buffer); + val1 = ioread16(dpld->dp_base); + + iowrite16(PLD_ADDR_MASK(&dpld->mbx2ap[0]), + dpld->address_buffer); + val2 = ioread16(dpld->dp_base); + + if (likely(val1 == val2)) { + spin_unlock_irqrestore(&dpld->pld_lock, flags); + return val1; + } + + mif_err("ERR: intr1(%d) != intr1(%d)\n", val1, val2); + + } while (cnt--); + + spin_unlock_irqrestore(&dpld->pld_lock, flags); + + return val1; +} + +static inline void send_intr(struct dpram_link_device *dpld, u16 mask) +{ + int cnt = 3; + u32 val = 0; + unsigned long int flags; + + spin_lock_irqsave(&dpld->pld_lock, flags); + + iowrite16(PLD_ADDR_MASK(&dpld->mbx2cp[0]), + dpld->address_buffer); + iowrite16((u16)mask, dpld->dp_base); + + do { + /* Check head value written */ + iowrite16(PLD_ADDR_MASK(&dpld->mbx2cp[0]), + dpld->address_buffer); + val = ioread16(dpld->dp_base); + + if (likely(val == mask)) { + spin_unlock_irqrestore(&dpld->pld_lock, flags); + return; + } + + mif_err("ERR: intr1(%d) != intr2(%d)\n", val, mask); + udelay(100); + + /* Write head value again */ + iowrite16(PLD_ADDR_MASK(&dpld->mbx2cp[0]), + dpld->address_buffer); + iowrite16((u16)mask, dpld->dp_base); + } while (cnt--); + + spin_unlock_irqrestore(&dpld->pld_lock, flags); + + return; +} + +static inline u16 get_magic(struct dpram_link_device *dpld) +{ + u16 val1 = 0, val2 = 0, cnt = 3; + unsigned long int flags; + + spin_lock_irqsave(&dpld->pld_lock, flags); + + do { + /* Check head value written */ + iowrite16(PLD_ADDR_MASK(&dpld->magic_ap2cp[0]), + dpld->address_buffer); + val1 = ioread16(dpld->dp_base); + + iowrite16(PLD_ADDR_MASK(&dpld->magic_ap2cp[0]), + dpld->address_buffer); + val2 = ioread16(dpld->dp_base); + + if (likely(val1 == val2)) { + spin_unlock_irqrestore(&dpld->pld_lock, flags); + return val1; + } + + mif_err("ERR: txq.head(%d) != in(%d)\n", val1, val2); + udelay(100); + + } while (cnt--); + + spin_unlock_irqrestore(&dpld->pld_lock, flags); + return val1; + +} + +static inline void set_magic(struct dpram_link_device *dpld, u16 in) +{ + int cnt = 3; + u32 val = 0; + unsigned long int flags; + + spin_lock_irqsave(&dpld->pld_lock, flags); + + iowrite16(PLD_ADDR_MASK(&dpld->magic_ap2cp[0]), + dpld->address_buffer); + iowrite16((u16)in, dpld->dp_base); + + do { + /* Check head value written */ + iowrite16(PLD_ADDR_MASK(&dpld->magic_ap2cp[0]), + dpld->address_buffer); + val = ioread16(dpld->dp_base); + + if (likely(val == in)) { + spin_unlock_irqrestore(&dpld->pld_lock, flags); + return; + } + + mif_err("ERR: magic1(%d) != magic2(%d)\n", val, in); + udelay(100); + + /* Write head value again */ + iowrite16(PLD_ADDR_MASK(&dpld->magic_ap2cp[0]), + dpld->address_buffer); + iowrite16((u16)in, dpld->dp_base); + } while (cnt--); + + spin_unlock_irqrestore(&dpld->pld_lock, flags); + return; +} + +static inline u16 get_access(struct dpram_link_device *dpld) +{ + u16 val1 = 0, val2 = 0, cnt = 3; + unsigned long int flags; + + spin_lock_irqsave(&dpld->pld_lock, flags); + + do { + /* Check head value written */ + iowrite16(PLD_ADDR_MASK(&dpld->access_ap2cp[0]), + dpld->address_buffer); + val1 = ioread16(dpld->dp_base); + + iowrite16(PLD_ADDR_MASK(&dpld->access_ap2cp[0]), + dpld->address_buffer); + val2 = ioread16(dpld->dp_base); + + if (likely(val1 == val2)) { + spin_unlock_irqrestore(&dpld->pld_lock, flags); + return val1; + } + + mif_err("ERR: access1(%d) != access2(%d)\n", val1, val2); + udelay(100); + + } while (cnt--); + + spin_unlock_irqrestore(&dpld->pld_lock, flags); + return val1; + +} + +static inline void set_access(struct dpram_link_device *dpld, u16 in) +{ + int cnt = 3; + u32 val = 0; + unsigned long int flags; + + iowrite16(PLD_ADDR_MASK(&dpld->access_ap2cp[0]), + dpld->address_buffer); + iowrite16((u16)in, dpld->dp_base); + + spin_lock_irqsave(&dpld->pld_lock, flags); + + do { + /* Check head value written */ + iowrite16(PLD_ADDR_MASK(&dpld->access_ap2cp[0]), + dpld->address_buffer); + val = ioread16(dpld->dp_base); + + if (likely(val == in)) { + spin_unlock_irqrestore(&dpld->pld_lock, flags); + return; + } + + mif_err("ERR: access(%d) != access(%d)\n", val, in); + udelay(100); + + /* Write head value again */ + iowrite16(PLD_ADDR_MASK(&dpld->access_ap2cp[0]), + dpld->address_buffer); + iowrite16((u16)in, dpld->dp_base); + } while (cnt--); + + spin_unlock_irqrestore(&dpld->pld_lock, flags); + return; +} + +static inline u32 get_tx_head(struct dpram_link_device *dpld, int id) +{ + u16 val1 = 0, val2 = 0, cnt = 3; + unsigned long int flags; + + spin_lock_irqsave(&dpld->pld_lock, flags); + + do { + /* Check head value written */ + iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->txq.head)[0]), + dpld->address_buffer); + val1 = ioread16(dpld->dp_base); + + iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->txq.head)[0]), + dpld->address_buffer); + val2 = ioread16(dpld->dp_base); + + if (likely(val1 == val2)) { + spin_unlock_irqrestore(&dpld->pld_lock, flags); + return val1; + } + + mif_err("ERR: %s txq.head(%d) != in(%d)\n", + get_dev_name(id), val1, val2); + udelay(100); + + } while (cnt--); + + spin_unlock_irqrestore(&dpld->pld_lock, flags); + return val1; +} + +static inline u32 get_tx_tail(struct dpram_link_device *dpld, int id) +{ + u16 val1 = 0, val2 = 0, cnt = 3; + unsigned long int flags; + + spin_lock_irqsave(&dpld->pld_lock, flags); + + do { + /* Check head value written */ + iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->txq.tail)[0]), + dpld->address_buffer); + val1 = ioread16(dpld->dp_base); + + iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->txq.tail)[0]), + dpld->address_buffer); + val2 = ioread16(dpld->dp_base); + + if (likely(val1 == val2)) { + spin_unlock_irqrestore(&dpld->pld_lock, flags); + return val1; + } + + mif_err("ERR: %s txq.tail(%d) != in(%d)\n", + get_dev_name(id), val1, val2); + udelay(100); + + } while (cnt--); + + spin_unlock_irqrestore(&dpld->pld_lock, flags); + return val1; +} + +static inline void set_tx_head(struct dpram_link_device *dpld, int id, u32 in) +{ + int cnt = 3; + u32 val = 0; + unsigned long int flags; + + spin_lock_irqsave(&dpld->pld_lock, flags); + + iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->txq.head)[0]), + dpld->address_buffer); + iowrite16((u16)in, dpld->dp_base); + + do { + /* Check head value written */ + iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->txq.head)[0]), + dpld->address_buffer); + val = ioread16(dpld->dp_base); + + if (likely(val == in)) { + spin_unlock_irqrestore(&dpld->pld_lock, flags); + return; + } + + mif_err("ERR: %s txq.head(%d) != in(%d)\n", + get_dev_name(id), val, in); + udelay(100); + + /* Write head value again */ + iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->txq.head)[0]), + dpld->address_buffer); + iowrite16((u16)in, dpld->dp_base); + } while (cnt--); + + spin_unlock_irqrestore(&dpld->pld_lock, flags); + return; +} + +static inline void set_tx_tail(struct dpram_link_device *dpld, int id, u32 out) +{ + return; +} + +static inline u8 *get_tx_buff(struct dpram_link_device *dpld, int id) +{ + return dpld->dev[id]->txq.buff; +} + +static inline u32 get_tx_buff_size(struct dpram_link_device *dpld, int id) +{ + return dpld->dev[id]->txq.size; +} + +static inline u32 get_rx_head(struct dpram_link_device *dpld, int id) +{ + u16 val1 = 0, val2 = 0, cnt = 3; + unsigned long int flags; + + spin_lock_irqsave(&dpld->pld_lock, flags); + + do { + /* Check head value written */ + iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->rxq.head)[0]), + dpld->address_buffer); + val1 = ioread16(dpld->dp_base); + + iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->rxq.head)[0]), + dpld->address_buffer); + val2 = ioread16(dpld->dp_base); + + if (likely(val1 == val2)) { + spin_unlock_irqrestore(&dpld->pld_lock, flags); + return val1; + } + + mif_err("ERR: %s rxq.head(%d) != in(%d)\n", + get_dev_name(id), val1, val2); + udelay(100); + + } while (cnt--); + + spin_unlock_irqrestore(&dpld->pld_lock, flags); + return val1; +} + +static inline u32 get_rx_tail(struct dpram_link_device *dpld, int id) +{ + u16 val1 = 0, val2 = 0, cnt = 3; + unsigned long int flags; + + spin_lock_irqsave(&dpld->pld_lock, flags); + + do { + /* Check head value written */ + iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->rxq.tail)[0]), + dpld->address_buffer); + val1 = ioread16(dpld->dp_base); + + iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->rxq.tail)[0]), + dpld->address_buffer); + val2 = ioread16(dpld->dp_base); + + if (likely(val1 == val2)) { + spin_unlock_irqrestore(&dpld->pld_lock, flags); + return val1; + } + + mif_err("ERR: %s rxq.tail(%d) != in(%d)\n", + get_dev_name(id), val1, val2); + udelay(100); + + } while (cnt--); + + spin_unlock_irqrestore(&dpld->pld_lock, flags); + return val1; +} + +static inline void set_rx_head(struct dpram_link_device *dpld, int id, u32 in) +{ + return; +} + +static inline void set_rx_tail(struct dpram_link_device *dpld, int id, u32 out) +{ + int cnt = 3; + u32 val = 0; + unsigned long int flags; + + spin_lock_irqsave(&dpld->pld_lock, flags); + + iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->rxq.tail)[0]), + dpld->address_buffer); + iowrite16((u16)out, dpld->dp_base); + + do { + /* Check tail value written */ + iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->rxq.tail)[0]), + dpld->address_buffer); + val = ioread16(dpld->dp_base); + + if (val == out) { + spin_unlock_irqrestore(&dpld->pld_lock, flags); + return; + } + + mif_err("ERR: %s rxq.tail(%d) != out(%d)\n", + get_dev_name(id), val, out); + udelay(100); + + /* Write tail value again */ + iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->rxq.tail)[0]), + dpld->address_buffer); + iowrite16((u16)out, dpld->dp_base); + } while (cnt--); + + spin_unlock_irqrestore(&dpld->pld_lock, flags); + return; +} + +static inline u8 *get_rx_buff(struct dpram_link_device *dpld, int id) +{ + return dpld->dev[id]->rxq.buff; +} + +static inline u32 get_rx_buff_size(struct dpram_link_device *dpld, int id) +{ + return dpld->dev[id]->rxq.size; +} + +static inline u16 get_mask_req_ack(struct dpram_link_device *dpld, int id) +{ + return dpld->dev[id]->mask_req_ack; +} + +static inline u16 get_mask_res_ack(struct dpram_link_device *dpld, int id) +{ + return dpld->dev[id]->mask_res_ack; +} + +static inline u16 get_mask_send(struct dpram_link_device *dpld, int id) +{ + return dpld->dev[id]->mask_send; +} + +static inline bool dpram_circ_valid(u32 size, u32 in, u32 out) +{ + if (in >= size) + return false; + + if (out >= size) + return false; + + return true; +} + +/* Get free space in the TXQ as well as in & out pointers */ +static inline int dpram_get_txq_space(struct dpram_link_device *dpld, int dev, + u32 qsize, u32 *in, u32 *out) +{ + struct link_device *ld = &dpld->ld; + int cnt = 3; + u32 head; + u32 tail; + int space; + + do { + head = get_tx_head(dpld, dev); + tail = get_tx_tail(dpld, dev); + + space = (head < tail) ? (tail - head - 1) : + (qsize + tail - head - 1); + mif_debug("%s: %s_TXQ qsize[%u] in[%u] out[%u] space[%u]\n", + ld->name, get_dev_name(dev), qsize, head, tail, space); + + if (dpram_circ_valid(qsize, head, tail)) { + *in = head; + *out = tail; + return space; + } + + mif_info("%s: CAUTION! <%pf> " + "%s_TXQ invalid (size:%d in:%d out:%d)\n", + ld->name, __builtin_return_address(0), + get_dev_name(dev), qsize, head, tail); + + udelay(100); + } while (cnt--); + + *in = 0; + *out = 0; + return -EINVAL; +} + +static void dpram_reset_tx_circ(struct dpram_link_device *dpld, int dev) +{ + set_tx_head(dpld, dev, 0); + set_tx_tail(dpld, dev, 0); + if (dev == IPC_FMT) + trigger_force_cp_crash(dpld); +} + +/* Get data size in the RXQ as well as in & out pointers */ +static inline int dpram_get_rxq_rcvd(struct dpram_link_device *dpld, int dev, + u32 qsize, u32 *in, u32 *out) +{ + struct link_device *ld = &dpld->ld; + int cnt = 3; + u32 head; + u32 tail; + u32 rcvd; + + do { + head = get_rx_head(dpld, dev); + tail = get_rx_tail(dpld, dev); + if (head == tail) { + *in = head; + *out = tail; + return 0; + } + + rcvd = (head > tail) ? (head - tail) : (qsize - tail + head); + mif_info("%s: %s_RXQ qsize[%u] in[%u] out[%u] rcvd[%u]\n", + ld->name, get_dev_name(dev), qsize, head, tail, rcvd); + + if (dpram_circ_valid(qsize, head, tail)) { + *in = head; + *out = tail; + return rcvd; + } + + mif_info("%s: CAUTION! <%pf> " + "%s_RXQ invalid (size:%d in:%d out:%d)\n", + ld->name, __builtin_return_address(0), + get_dev_name(dev), qsize, head, tail); + + udelay(100); + } while (cnt--); + + *in = 0; + *out = 0; + return -EINVAL; +} + +static void dpram_reset_rx_circ(struct dpram_link_device *dpld, int dev) +{ + set_rx_head(dpld, dev, 0); + set_rx_tail(dpld, dev, 0); + if (dev == IPC_FMT) + trigger_force_cp_crash(dpld); +} + +/* +** CAUTION : dpram_allow_sleep() MUST be invoked after dpram_wake_up() success +*/ +static int dpram_wake_up(struct dpram_link_device *dpld) +{ + struct link_device *ld = &dpld->ld; + + if (!dpld->dpctl->wakeup) + return 0; + + if (dpld->dpctl->wakeup() < 0) { + mif_err("%s: ERR! <%pf> DPRAM wakeup fail\n", + ld->name, __builtin_return_address(0)); + return -EACCES; + } + + atomic_inc(&dpld->accessing); + return 0; +} + +static void dpram_allow_sleep(struct dpram_link_device *dpld) +{ + struct link_device *ld = &dpld->ld; + + if (!dpld->dpctl->sleep) + return; + + if (atomic_dec_return(&dpld->accessing) <= 0) { + dpld->dpctl->sleep(); + atomic_set(&dpld->accessing, 0); + mif_debug("%s: DPRAM sleep possible\n", ld->name); + } +} + +static int dpram_check_access(struct dpram_link_device *dpld) +{ + struct link_device *ld = &dpld->ld; + int i; + u16 magic = get_magic(dpld); + u16 access = get_access(dpld); + + if (likely(magic == DPRAM_MAGIC_CODE && access == 1)) + return 0; + + for (i = 1; i <= 100; i++) { + mif_info("%s: ERR! magic:%X access:%X -> retry:%d\n", + ld->name, magic, access, i); + udelay(100); + + magic = get_magic(dpld); + access = get_access(dpld); + if (likely(magic == DPRAM_MAGIC_CODE && access == 1)) + return 0; + } + + mif_info("%s: !CRISIS! magic:%X access:%X\n", ld->name, magic, access); + return -EACCES; +} + +static bool dpram_ipc_active(struct dpram_link_device *dpld) +{ + struct link_device *ld = &dpld->ld; + + /* Check DPRAM mode */ + if (ld->mode != LINK_MODE_IPC) { + mif_info("%s: <%pf> ld->mode != LINK_MODE_IPC\n", + ld->name, __builtin_return_address(0)); + return false; + } + + if (dpram_check_access(dpld) < 0) { + mif_info("%s: ERR! <%pf> dpram_check_access fail\n", + ld->name, __builtin_return_address(0)); + return false; + } + + return true; +} + +static void dpram_ipc_write(struct dpram_link_device *dpld, int dev, + u32 qsize, u32 in, u32 out, struct sk_buff *skb) +{ + struct link_device *ld = &dpld->ld; + u8 __iomem *buff = get_tx_buff(dpld, dev); + u8 *src = skb->data; + u32 len = skb->len; + u32 inp; + struct mif_irq_map map; + + if (in < out) { + /* +++++++++ in ---------- out ++++++++++ */ + iowrite16(PLD_ADDR_MASK(&(buff+in)[0]), dpld->address_buffer); + memcpy(dpld->dp_base, src, len); + } else { + /* ------ out +++++++++++ in ------------ */ + u32 space = qsize - in; + + /* 1) in -> buffer end */ + iowrite16(PLD_ADDR_MASK(&(buff+in)[0]), dpld->address_buffer); + memcpy(dpld->dp_base, src, ((len > space) ? space : len)); + + if (len > space) { + iowrite16(PLD_ADDR_MASK(&buff[0]), + dpld->address_buffer); + memcpy(dpld->dp_base, (src+space), (len-space)); + } + } + + /* update new in pointer */ + inp = in + len; + if (inp >= qsize) + inp -= qsize; + set_tx_head(dpld, dev, inp); + + if (dev == IPC_FMT) { + set_dpram_map(dpld, &map); + mif_irq_log(ld->mc->msd, map, "ipc_write", sizeof("ipc_write")); + mif_ipc_log(MIF_IPC_AP2CP, ld->mc->msd, skb->data, skb->len); + } +} + +static int dpram_try_ipc_tx(struct dpram_link_device *dpld, int dev) +{ + struct link_device *ld = &dpld->ld; + struct sk_buff_head *txq = ld->skb_txq[dev]; + struct sk_buff *skb; + unsigned long int flags; + u32 qsize = get_tx_buff_size(dpld, dev); + u32 in; + u32 out; + int space; + int copied = 0; + + spin_lock_irqsave(&dpld->tx_rx_lock, flags); + + while (1) { + space = dpram_get_txq_space(dpld, dev, qsize, &in, &out); + if (unlikely(space < 0)) { + spin_unlock_irqrestore(&dpld->tx_rx_lock, flags); + dpram_reset_tx_circ(dpld, dev); + return space; + } + + skb = skb_dequeue(txq); + if (unlikely(!skb)) + break; + + if (unlikely(space < skb->len)) { + atomic_set(&dpld->res_required[dev], 1); + skb_queue_head(txq, skb); + spin_unlock_irqrestore(&dpld->tx_rx_lock, flags); + mif_info("%s: %s " + "qsize[%u] in[%u] out[%u] free[%u] < len[%u]\n", + ld->name, get_dev_name(dev), + qsize, in, out, space, skb->len); + return -ENOSPC; + } + + /* TX if there is enough room in the queue */ + dpram_ipc_write(dpld, dev, qsize, in, out, skb); + copied += skb->len; + dev_kfree_skb_any(skb); + } + + spin_unlock_irqrestore(&dpld->tx_rx_lock, flags); + + return copied; +} + +static void dpram_ipc_rx_task(unsigned long data) +{ + struct dpram_link_device *dpld = (struct dpram_link_device *)data; + struct link_device *ld = &dpld->ld; + struct io_device *iod; + struct dpram_rxb *rxb; + unsigned qlen; + int i; + + for (i = 0; i < dpld->max_ipc_dev; i++) { + iod = dpld->iod[i]; + qlen = rxbq_size(&dpld->rxbq[i]); + while (qlen > 0) { + rxb = rxbq_get_data_rxb(&dpld->rxbq[i]); + iod->recv(iod, ld, rxb->data, rxb->len); + rxb_clear(rxb); + qlen--; + } + } +} + +static void dpram_ipc_read(struct dpram_link_device *dpld, int dev, u8 *dst, + u8 __iomem *src, u32 out, u32 len, u32 qsize) +{ + u8 *ori_det = dst; + unsigned long flags; + + if ((out + len) <= qsize) { + /* ----- (out) (in) ----- */ + /* ----- 7f 00 00 7e ----- */ + iowrite16(PLD_ADDR_MASK(&(src+out)[0]), dpld->address_buffer); + memcpy(dst, dpld->dp_base, len); + } else { + /* (in) ----------- (out) */ + /* 00 7e ----------- 7f 00 */ + unsigned len1 = qsize - out; + + /* 1) out -> buffer end */ + iowrite16(PLD_ADDR_MASK(&(src+out)[0]), dpld->address_buffer); + memcpy(dst, dpld->dp_base, len1); + + /* 2) buffer start -> in */ + dst += len1; + iowrite16(PLD_ADDR_MASK(&src[0]), dpld->address_buffer); + memcpy(dst, dpld->dp_base, (len - len1)); + } + + if (dpld->ld.mode == LINK_MODE_IPC && ori_det[0] != 0x7F) { + mif_info("ipc read error!! in[%d], out[%d]\n", + get_rx_head(dpld, dev), + get_rx_tail(dpld, dev)); + } + +} + +/* + ret < 0 : error + ret == 0 : no data + ret > 0 : valid data +*/ +static int dpram_ipc_recv_data_with_rxb(struct dpram_link_device *dpld, int dev) +{ + struct link_device *ld = &dpld->ld; + struct dpram_rxb *rxb; + u8 __iomem *src = get_rx_buff(dpld, dev); + u32 qsize = get_rx_buff_size(dpld, dev); + u32 in; + u32 out; + u32 rcvd; + struct mif_irq_map map; + unsigned long int flags; + + spin_lock_irqsave(&dpld->tx_rx_lock, flags); + + rcvd = dpram_get_rxq_rcvd(dpld, dev, qsize, &in, &out); + if (rcvd <= 0) { + spin_unlock_irqrestore(&dpld->tx_rx_lock, flags); + return rcvd; + } + + if (dev == IPC_FMT) { + set_dpram_map(dpld, &map); + mif_irq_log(ld->mc->msd, map, "ipc_recv", sizeof("ipc_recv")); + } + + /* Allocate an rxb */ + rxb = rxbq_get_free_rxb(&dpld->rxbq[dev]); + if (!rxb) { + mif_info("%s: ERR! %s rxbq_get_free_rxb fail\n", + ld->name, get_dev_name(dev)); + spin_unlock_irqrestore(&dpld->tx_rx_lock, flags); + return -ENOMEM; + } + + /* Read data from each DPRAM buffer */ + dpram_ipc_read(dpld, dev, rxb_put(rxb, rcvd), src, out, rcvd, qsize); + + /* Calculate and set new out */ + out += rcvd; + if (out >= qsize) + out -= qsize; + set_rx_tail(dpld, dev, out); + + spin_unlock_irqrestore(&dpld->tx_rx_lock, flags); + return rcvd; +} + +/* + ret < 0 : error + ret == 0 : no data + ret > 0 : valid data +*/ +static int dpram_ipc_recv_data_with_skb(struct dpram_link_device *dpld, int dev) +{ + struct link_device *ld = &dpld->ld; + struct io_device *iod = dpld->iod[dev]; + struct sk_buff *skb; + u8 __iomem *src = get_rx_buff(dpld, dev); + u32 qsize = get_rx_buff_size(dpld, dev); + u32 in; + u32 out; + u32 rcvd; + int rest; + u8 *frm; + u8 *dst; + unsigned int len; + unsigned int pad; + unsigned int tot; + struct mif_irq_map map; + + rcvd = dpram_get_rxq_rcvd(dpld, dev, qsize, &in, &out); + if (rcvd <= 0) + return rcvd; + + if (dev == IPC_FMT) { + set_dpram_map(dpld, &map); + mif_irq_log(ld->mc->msd, map, "ipc_recv", sizeof("ipc_recv")); + } + + rest = rcvd; + while (rest > 0) { + frm = src + out; + if (unlikely(!sipc5_start_valid(frm[0]))) { + mif_err("%s: ERR! %s invalid start 0x%02X\n", + ld->name, get_dev_name(dev), frm[0]); + skb_queue_purge(&dpld->skb_rxq[dev]); + return -EBADMSG; + } + + len = sipc5_get_frame_sz16(frm); + if (unlikely(len > rest)) { + mif_err("%s: ERR! %s len %d > rest %d\n", + ld->name, get_dev_name(dev), len, rest); + skb_queue_purge(&dpld->skb_rxq[dev]); + return -EBADMSG; + } + + pad = sipc5_calc_padding_size(len); + tot = len + pad; + + /* Allocate an skb */ + skb = dev_alloc_skb(tot); + if (!skb) { + mif_err("%s: ERR! %s dev_alloc_skb fail\n", + ld->name, get_dev_name(dev)); + return -ENOMEM; + } + + /* Read data from each DPRAM buffer */ + dst = skb_put(skb, tot); + dpram_ipc_read(dpld, dev, dst, src, out, tot, qsize); + skb_trim(skb, len); + iod->recv_skb(iod, ld, skb); + + /* Calculate and set new out */ + rest -= tot; + out += tot; + if (out >= qsize) + out -= qsize; + } + + set_rx_tail(dpld, dev, out); + + return rcvd; +} + +static void non_command_handler(struct dpram_link_device *dpld, u16 non_cmd) +{ + struct link_device *ld = &dpld->ld; + int i = 0; + int ret = 0; + u16 tx_mask = 0; + + if (!dpram_ipc_active(dpld)) + return; + + /* Read data from DPRAM */ + for (i = 0; i < dpld->max_ipc_dev; i++) { + if (dpld->use_skb) + ret = dpram_ipc_recv_data_with_skb(dpld, i); + else + ret = dpram_ipc_recv_data_with_rxb(dpld, i); + if (ret < 0) + dpram_reset_rx_circ(dpld, i); + + /* Check and process REQ_ACK (at this time, in == out) */ + if (non_cmd & get_mask_req_ack(dpld, i)) { + mif_debug("%s: send %s_RES_ACK\n", + ld->name, get_dev_name(i)); + tx_mask |= get_mask_res_ack(dpld, i); + } + } + + if (!dpld->use_skb) { + /* Schedule soft IRQ for RX */ + tasklet_hi_schedule(&dpld->rx_tsk); + } + + /* Try TX via DPRAM */ + for (i = 0; i < dpld->max_ipc_dev; i++) { + if (atomic_read(&dpld->res_required[i]) > 0) { + ret = dpram_try_ipc_tx(dpld, i); + if (ret > 0) { + atomic_set(&dpld->res_required[i], 0); + tx_mask |= get_mask_send(dpld, i); + } else if (ret == -ENOSPC) { + tx_mask |= get_mask_req_ack(dpld, i); + } + } + } + + if (tx_mask) { + send_intr(dpld, INT_NON_CMD(tx_mask)); + mif_debug("%s: send intr 0x%04X\n", ld->name, tx_mask); + } +} + +static void handle_cp_crash(struct dpram_link_device *dpld) +{ + struct link_device *ld = &dpld->ld; + struct io_device *iod; + int i; + + for (i = 0; i < dpld->max_ipc_dev; i++) { + mif_info("%s: purging %s_skb_txq\b", ld->name, get_dev_name(i)); + skb_queue_purge(ld->skb_txq[i]); + } + + iod = link_get_iod_with_format(ld, IPC_FMT); + iod->modem_state_changed(iod, STATE_CRASH_EXIT); + + iod = link_get_iod_with_format(ld, IPC_BOOT); + iod->modem_state_changed(iod, STATE_CRASH_EXIT); + + iod = link_get_iod_with_channel(ld, PS_DATA_CH_0); + if (iod) + iodevs_for_each(iod->msd, iodev_netif_stop, 0); +} + +static void handle_no_crash_ack(unsigned long arg) +{ + struct dpram_link_device *dpld = (struct dpram_link_device *)arg; + struct link_device *ld = &dpld->ld; + + mif_err("%s: ERR! No CRASH_EXIT ACK from CP\n", ld->mc->name); + + if (!wake_lock_active(&dpld->wlock)) + wake_lock(&dpld->wlock); + + handle_cp_crash(dpld); +} + +static int trigger_force_cp_crash(struct dpram_link_device *dpld) +{ + struct link_device *ld = &dpld->ld; + + if (ld->mode == LINK_MODE_ULOAD) { + mif_err("%s: CP crash is already in progress\n", ld->mc->name); + return 0; + } + + ld->mode = LINK_MODE_ULOAD; + mif_err("%s: called by %pf\n", ld->name, __builtin_return_address(0)); + + dpram_wake_up(dpld); + + send_intr(dpld, INT_CMD(INT_CMD_CRASH_EXIT)); + + mif_add_timer(&dpld->crash_ack_timer, FORCE_CRASH_ACK_TIMEOUT, + handle_no_crash_ack, (unsigned long)dpld); + + return 0; +} + +static int dpram_init_ipc(struct dpram_link_device *dpld) +{ + struct link_device *ld = &dpld->ld; + int i; + + if (ld->mode == LINK_MODE_IPC && + get_magic(dpld) == DPRAM_MAGIC_CODE && + get_access(dpld) == 1) + mif_info("%s: IPC already initialized\n", ld->name); + + /* Clear pointers in every circular queue */ + for (i = 0; i < dpld->max_ipc_dev; i++) { + set_tx_head(dpld, i, 0); + set_tx_tail(dpld, i, 0); + set_rx_head(dpld, i, 0); + set_rx_tail(dpld, i, 0); + } + + /* Initialize variables for efficient TX/RX processing */ + for (i = 0; i < dpld->max_ipc_dev; i++) + dpld->iod[i] = link_get_iod_with_format(ld, i); + dpld->iod[IPC_RAW] = link_get_iod_with_format(ld, IPC_MULTI_RAW); + + if (dpld->iod[IPC_RAW]->recv_skb) + dpld->use_skb = true; + + for (i = 0; i < dpld->max_ipc_dev; i++) { + atomic_set(&dpld->res_required[i], 0); + skb_queue_purge(&dpld->skb_rxq[i]); + } + + spin_lock_init(&dpld->tx_rx_lock); + + /* Enable IPC */ + atomic_set(&dpld->accessing, 0); + + set_magic(dpld, DPRAM_MAGIC_CODE); + set_access(dpld, 1); + if (get_magic(dpld) != DPRAM_MAGIC_CODE || get_access(dpld) != 1) + return -EACCES; + + ld->mode = LINK_MODE_IPC; + + if (wake_lock_active(&dpld->wlock)) + wake_unlock(&dpld->wlock); + + return 0; +} + +static void cmd_req_active_handler(struct dpram_link_device *dpld) +{ + send_intr(dpld, INT_CMD(INT_CMD_RES_ACTIVE)); +} + +static void cmd_crash_reset_handler(struct dpram_link_device *dpld) +{ + struct link_device *ld = &dpld->ld; + struct io_device *iod = NULL; + + ld->mode = LINK_MODE_ULOAD; + + if (!wake_lock_active(&dpld->wlock)) + wake_lock(&dpld->wlock); + + mif_err("%s: Recv 0xC7 (CRASH_RESET)\n", ld->name); + + iod = link_get_iod_with_format(ld, IPC_FMT); + iod->modem_state_changed(iod, STATE_CRASH_RESET); + + iod = link_get_iod_with_format(ld, IPC_BOOT); + iod->modem_state_changed(iod, STATE_CRASH_RESET); +} + +static void cmd_crash_exit_handler(struct dpram_link_device *dpld) +{ + struct link_device *ld = &dpld->ld; + + ld->mode = LINK_MODE_ULOAD; + + if (!wake_lock_active(&dpld->wlock)) + wake_lock(&dpld->wlock); + + mif_err("%s: Recv 0xC9 (CRASH_EXIT)\n", ld->name); + + dpram_wake_up(dpld); + + del_timer(&dpld->crash_ack_timer); + + if (dpld->ext_op && dpld->ext_op->crash_log) + dpld->ext_op->crash_log(dpld); + + handle_cp_crash(dpld); +} + +static void cmd_phone_start_handler(struct dpram_link_device *dpld) +{ + struct link_device *ld = &dpld->ld; + struct io_device *iod = NULL; + + mif_info("%s: Recv 0xC8 (CP_START)\n", ld->name); + + dpram_init_ipc(dpld); + + iod = link_get_iod_with_format(ld, IPC_FMT); + if (!iod) { + mif_info("%s: ERR! no iod\n", ld->name); + return; + } + + if (dpld->ext_op && dpld->ext_op->cp_start_handler) + dpld->ext_op->cp_start_handler(dpld); + + if (ld->mc->phone_state != STATE_ONLINE) { + mif_info("%s: phone_state: %d -> ONLINE\n", + ld->name, ld->mc->phone_state); + iod->modem_state_changed(iod, STATE_ONLINE); + } + + mif_info("%s: Send 0xC2 (INIT_END)\n", ld->name); + send_intr(dpld, INT_CMD(INT_CMD_INIT_END)); +} + +static void command_handler(struct dpram_link_device *dpld, u16 cmd) +{ + struct link_device *ld = &dpld->ld; + + switch (INT_CMD_MASK(cmd)) { + case INT_CMD_REQ_ACTIVE: + cmd_req_active_handler(dpld); + break; + + case INT_CMD_CRASH_RESET: + dpld->dpram_init_status = DPRAM_INIT_STATE_NONE; + cmd_crash_reset_handler(dpld); + break; + + case INT_CMD_CRASH_EXIT: + dpld->dpram_init_status = DPRAM_INIT_STATE_NONE; + cmd_crash_exit_handler(dpld); + break; + + case INT_CMD_PHONE_START: + dpld->dpram_init_status = DPRAM_INIT_STATE_READY; + cmd_phone_start_handler(dpld); + complete_all(&dpld->dpram_init_cmd); + break; + + case INT_CMD_NV_REBUILDING: + mif_info("%s: NV_REBUILDING\n", ld->name); + break; + + case INT_CMD_PIF_INIT_DONE: + complete_all(&dpld->modem_pif_init_done); + break; + + case INT_CMD_SILENT_NV_REBUILDING: + mif_info("%s: SILENT_NV_REBUILDING\n", ld->name); + break; + + case INT_CMD_NORMAL_PWR_OFF: + /*ToDo:*/ + /*kernel_sec_set_cp_ack()*/; + break; + + case INT_CMD_REQ_TIME_SYNC: + case INT_CMD_CP_DEEP_SLEEP: + case INT_CMD_EMER_DOWN: + break; + + default: + mif_info("%s: unknown command 0x%04X\n", ld->name, cmd); + } +} + +static void ext_command_handler(struct dpram_link_device *dpld, u16 cmd) +{ + struct link_device *ld = &dpld->ld; + u16 resp; + + switch (EXT_CMD_MASK(cmd)) { + case EXT_CMD_SET_SPEED_LOW: + if (dpld->dpctl->setup_speed) { + dpld->dpctl->setup_speed(DPRAM_SPEED_LOW); + resp = INT_EXT_CMD(EXT_CMD_SET_SPEED_LOW); + send_intr(dpld, resp); + } + break; + + case EXT_CMD_SET_SPEED_MID: + if (dpld->dpctl->setup_speed) { + dpld->dpctl->setup_speed(DPRAM_SPEED_MID); + resp = INT_EXT_CMD(EXT_CMD_SET_SPEED_MID); + send_intr(dpld, resp); + } + break; + + case EXT_CMD_SET_SPEED_HIGH: + if (dpld->dpctl->setup_speed) { + dpld->dpctl->setup_speed(DPRAM_SPEED_HIGH); + resp = INT_EXT_CMD(EXT_CMD_SET_SPEED_HIGH); + send_intr(dpld, resp); + } + break; + + default: + mif_info("%s: unknown command 0x%04X\n", ld->name, cmd); + break; + } +} + +static void udl_command_handler(struct dpram_link_device *dpld, u16 cmd) +{ + struct link_device *ld = &dpld->ld; + + if (cmd & UDL_RESULT_FAIL) { + mif_info("%s: ERR! Command failed: %04x\n", ld->name, cmd); + return; + } + + switch (UDL_CMD_MASK(cmd)) { + case UDL_CMD_RECV_READY: + mif_debug("%s: Send CP-->AP RECEIVE_READY\n", ld->name); + send_intr(dpld, CMD_IMG_START_REQ); + break; + default: + complete_all(&dpld->udl_cmd_complete); + } +} + +static irqreturn_t dpram_irq_handler(int irq, void *data) +{ + struct dpram_link_device *dpld = (struct dpram_link_device *)data; + struct link_device *ld = (struct link_device *)&dpld->ld; + u16 int2ap = 0; + + if (unlikely(ld->mode == LINK_MODE_OFFLINE)) + return IRQ_HANDLED; + + if (dpram_wake_up(dpld) < 0) { + log_dpram_status(dpld); + trigger_force_cp_crash(dpld); + return IRQ_HANDLED; + } + + int2ap = recv_intr(dpld); + + if (unlikely(int2ap == INT_POWERSAFE_FAIL)) { + mif_info("%s: int2ap == INT_POWERSAFE_FAIL\n", ld->name); + goto exit; + } else if (int2ap == 0x1234 || int2ap == 0xDBAB || int2ap == 0xABCD) { + if (dpld->ext_op && dpld->ext_op->dload_cmd_handler) { + dpld->ext_op->dload_cmd_handler(dpld, int2ap); + goto exit; + } + } + + if (unlikely(EXT_UDL_CMD(int2ap))) { + if (likely(EXT_INT_VALID(int2ap))) { + if (UDL_CMD_VALID(int2ap)) + udl_command_handler(dpld, int2ap); + else if (EXT_CMD_VALID(int2ap)) + ext_command_handler(dpld, int2ap); + else + mif_info("%s: ERR! invalid intr 0x%04X\n", + ld->name, int2ap); + } else { + mif_info("%s: ERR! invalid intr 0x%04X\n", + ld->name, int2ap); + } + } else { + if (likely(INT_VALID(int2ap))) { + if (unlikely(INT_CMD_VALID(int2ap))) + command_handler(dpld, int2ap); + else + non_command_handler(dpld, int2ap); + } else { + mif_info("%s: ERR! invalid intr 0x%04X\n", + ld->name, int2ap); + } + } + +exit: + clear_intr(dpld); + dpram_allow_sleep(dpld); + return IRQ_HANDLED; +} + +static void dpram_send_ipc(struct link_device *ld, int dev, + struct io_device *iod, struct sk_buff *skb) +{ + struct dpram_link_device *dpld = to_dpram_link_device(ld); + struct sk_buff_head *txq = ld->skb_txq[dev]; + int ret; + u16 mask; + + skb_queue_tail(txq, skb); + if (txq->qlen > 1024) { + mif_debug("%s: %s txq->qlen %d > 1024\n", + ld->name, get_dev_name(dev), txq->qlen); + } + + if (dpram_wake_up(dpld) < 0) { + trigger_force_cp_crash(dpld); + return; + } + + if (!dpram_ipc_active(dpld)) + goto exit; + + if (atomic_read(&dpld->res_required[dev]) > 0) { + mif_debug("%s: %s_TXQ is full\n", ld->name, get_dev_name(dev)); + goto exit; + } + + ret = dpram_try_ipc_tx(dpld, dev); + if (ret > 0) { + mask = get_mask_send(dpld, dev); + send_intr(dpld, INT_NON_CMD(mask)); + } else if (ret == -ENOSPC) { + mask = get_mask_req_ack(dpld, dev); + send_intr(dpld, INT_NON_CMD(mask)); + mif_info("%s: Send REQ_ACK 0x%04X\n", ld->name, mask); + } else { + mif_info("%s: dpram_try_ipc_tx fail (err %d)\n", ld->name, ret); + } + +exit: + dpram_allow_sleep(dpld); +} + +static int dpram_download_bin(struct link_device *ld, struct sk_buff *skb) +{ + struct dpram_link_device *dpld = to_dpram_link_device(ld); + + if (dpld->ext_op && dpld->ext_op->dload_bin) + return dpld->ext_op->dload_bin(dpld, skb); + else + return -ENODEV; +} + +static int dpram_send(struct link_device *ld, struct io_device *iod, + struct sk_buff *skb) +{ + enum dev_format dev = iod->format; + int len = skb->len; + + switch (dev) { + case IPC_FMT: + case IPC_RAW: + case IPC_RFS: + if (likely(ld->mode == LINK_MODE_IPC)) { + dpram_send_ipc(ld, dev, iod, skb); + } else { + mif_info("%s: ld->mode != LINK_MODE_IPC\n", ld->name); + dev_kfree_skb_any(skb); + } + return len; + + case IPC_BOOT: + return dpram_download_bin(ld, skb); + + default: + mif_info("%s: ERR! no TXQ for %s\n", ld->name, iod->name); + dev_kfree_skb_any(skb); + return -ENODEV; + } +} + +static int dpram_force_dump(struct link_device *ld, struct io_device *iod) +{ + struct dpram_link_device *dpld = to_dpram_link_device(ld); + trigger_force_cp_crash(dpld); + return 0; +} + +static void dpram_dump_memory(struct link_device *ld, char *buff) +{ + struct dpram_link_device *dpld = to_dpram_link_device(ld); + u8 __iomem *base = dpld->dpctl->dp_base; + u32 size = dpld->dpctl->dp_size; + + dpram_wake_up(dpld); + memcpy(buff, base, size); +} + +static int dpram_dump_start(struct link_device *ld, struct io_device *iod) +{ + struct dpram_link_device *dpld = to_dpram_link_device(ld); + + if (dpld->ext_op && dpld->ext_op->dump_start) + return dpld->ext_op->dump_start(dpld); + else + return -ENODEV; +} + +static int dpram_dump_update(struct link_device *ld, struct io_device *iod, + unsigned long arg) +{ + struct dpram_link_device *dpld = to_dpram_link_device(ld); + + if (dpld->ext_op && dpld->ext_op->dump_update) + return dpld->ext_op->dump_update(dpld, (void *)arg); + else + return -ENODEV; +} + +static int dpram_ioctl(struct link_device *ld, struct io_device *iod, + unsigned int cmd, unsigned long arg) +{ + struct dpram_link_device *dpld = to_dpram_link_device(ld); + int err = 0; + +/* + mif_info("%s: cmd 0x%08X\n", ld->name, cmd); +*/ + + switch (cmd) { + case IOCTL_DPRAM_INIT_STATUS: + mif_debug("%s: get dpram init status\n", ld->name); + return dpld->dpram_init_status; + + default: + if (dpld->ext_ioctl) { + err = dpld->ext_ioctl(dpld, iod, cmd, arg); + } else { + mif_err("%s: ERR! invalid cmd 0x%08X\n", ld->name, cmd); + err = -EINVAL; + } + + break; + } + + return err; +} + +static int dpram_table_init(struct dpram_link_device *dpld) +{ + struct link_device *ld = &dpld->ld; + u8 __iomem *dp_base; + int i; + + if (!dpld->dp_base) { + mif_info("%s: ERR! dpld->dp_base == NULL\n", ld->name); + return -EINVAL; + } + dp_base = dpld->dp_base; + + /* Map for IPC */ + if (dpld->dpctl->ipc_map) { + memcpy(&dpld->ipc_map, dpld->dpctl->ipc_map, + sizeof(struct dpram_ipc_map)); + } + + dpld->magic_ap2cp = dpld->ipc_map.magic_ap2cp; + dpld->access_ap2cp = dpld->ipc_map.access_ap2cp; + + dpld->magic_cp2ap = dpld->ipc_map.magic_cp2ap; + dpld->access_cp2ap = dpld->ipc_map.access_cp2ap; + + dpld->address_buffer = dpld->ipc_map.address_buffer; + + for (i = 0; i < dpld->max_ipc_dev; i++) + dpld->dev[i] = &dpld->ipc_map.dev[i]; + dpld->mbx2ap = dpld->ipc_map.mbx_cp2ap; + dpld->mbx2cp = dpld->ipc_map.mbx_ap2cp; + + /* Map for booting */ + if (dpld->ext_op && dpld->ext_op->init_boot_map) { + dpld->ext_op->init_boot_map(dpld); + } else { + dpld->bt_map.magic = (u32 *)(dp_base); + dpld->bt_map.buff = (u8 *)(dp_base + DP_BOOT_BUFF_OFFSET); + dpld->bt_map.size = dpld->dp_size - 8; + } + + /* Map for download (FOTA, UDL, etc.) */ + if (dpld->ext_op && dpld->ext_op->init_dl_map) { + dpld->ext_op->init_dl_map(dpld); + } else { + dpld->dl_map.magic = (u32 *)(dp_base); + dpld->dl_map.buff = (u8 *)(dp_base + DP_DLOAD_BUFF_OFFSET); + } + + /* Map for upload mode */ + if (dpld->ext_op && dpld->ext_op->init_ul_map) { + dpld->ext_op->init_ul_map(dpld); + } else { + dpld->ul_map.magic = (u32 *)(dp_base); + dpld->ul_map.buff = (u8 *)(dp_base + DP_ULOAD_BUFF_OFFSET); + } + + return 0; +} + +static void dpram_setup_common_op(struct dpram_link_device *dpld) +{ + dpld->clear_intr = clear_intr; + dpld->recv_intr = recv_intr; + dpld->send_intr = send_intr; + dpld->get_magic = get_magic; + dpld->set_magic = set_magic; + dpld->get_access = get_access; + dpld->set_access = set_access; + dpld->get_tx_head = get_tx_head; + dpld->get_tx_tail = get_tx_tail; + dpld->set_tx_head = set_tx_head; + dpld->set_tx_tail = set_tx_tail; + dpld->get_tx_buff = get_tx_buff; + dpld->get_tx_buff_size = get_tx_buff_size; + dpld->get_rx_head = get_rx_head; + dpld->get_rx_tail = get_rx_tail; + dpld->set_rx_head = set_rx_head; + dpld->set_rx_tail = set_rx_tail; + dpld->get_rx_buff = get_rx_buff; + dpld->get_rx_buff_size = get_rx_buff_size; + dpld->get_mask_req_ack = get_mask_req_ack; + dpld->get_mask_res_ack = get_mask_res_ack; + dpld->get_mask_send = get_mask_send; +} + +static int dpram_link_init(struct link_device *ld, struct io_device *iod) +{ + return 0; +} + +static void dpram_link_terminate(struct link_device *ld, struct io_device *iod) +{ + return; +} + +struct link_device *pld_create_link_device(struct platform_device *pdev) +{ + struct modem_data *mdm_data = NULL; + struct dpram_link_device *dpld = NULL; + struct link_device *ld = NULL; + struct resource *res = NULL; + resource_size_t res_size; + struct modemlink_dpram_control *dpctl = NULL; + unsigned long task_data; + int ret = 0; + int i = 0; + int bsize; + int qsize; + + /* Get the platform data */ + mdm_data = (struct modem_data *)pdev->dev.platform_data; + if (!mdm_data) { + mif_info("ERR! mdm_data == NULL\n"); + goto err; + } + mif_info("modem = %s\n", mdm_data->name); + mif_info("link device = %s\n", mdm_data->link_name); + + if (!mdm_data->dpram_ctl) { + mif_info("ERR! mdm_data->dpram_ctl == NULL\n"); + goto err; + } + dpctl = mdm_data->dpram_ctl; + + /* Alloc DPRAM link device structure */ + dpld = kzalloc(sizeof(struct dpram_link_device), GFP_KERNEL); + if (!dpld) { + mif_info("ERR! kzalloc dpld fail\n"); + goto err; + } + ld = &dpld->ld; + + /* Retrieve modem data and DPRAM control data from the modem data */ + ld->mdm_data = mdm_data; + ld->name = mdm_data->link_name; + ld->ipc_version = mdm_data->ipc_version; + + /* Retrieve the most basic data for IPC from the modem data */ + dpld->dpctl = dpctl; + dpld->dp_type = dpctl->dp_type; + + if (mdm_data->ipc_version < SIPC_VER_50) { + if (!dpctl->max_ipc_dev) { + mif_info("ERR! no max_ipc_dev\n"); + goto err; + } + + ld->aligned = dpctl->aligned; + dpld->max_ipc_dev = dpctl->max_ipc_dev; + } else { + ld->aligned = 1; + dpld->max_ipc_dev = MAX_SIPC5_DEV; + } + + /* Set attributes as a link device */ + ld->init_comm = dpram_link_init; + ld->terminate_comm = dpram_link_terminate; + ld->send = dpram_send; + ld->force_dump = dpram_force_dump; + ld->dump_start = dpram_dump_start; + ld->dump_update = dpram_dump_update; + ld->ioctl = dpram_ioctl; + + INIT_LIST_HEAD(&ld->list); + + skb_queue_head_init(&ld->sk_fmt_tx_q); + skb_queue_head_init(&ld->sk_raw_tx_q); + skb_queue_head_init(&ld->sk_rfs_tx_q); + ld->skb_txq[IPC_FMT] = &ld->sk_fmt_tx_q; + ld->skb_txq[IPC_RAW] = &ld->sk_raw_tx_q; + ld->skb_txq[IPC_RFS] = &ld->sk_rfs_tx_q; + + /* Set up function pointers */ + dpram_setup_common_op(dpld); + dpld->dpram_dump = dpram_dump_memory; + dpld->ext_op = dpram_get_ext_op(mdm_data->modem_type); + if (dpld->ext_op && dpld->ext_op->ioctl) + dpld->ext_ioctl = dpld->ext_op->ioctl; + + /* Retrieve DPRAM resource */ + if (!dpctl->dp_base) { + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + mif_info("%s: ERR! platform_get_resource fail\n", + ld->name); + goto err; + } + res_size = resource_size(res); + + dpctl->dp_base = ioremap_nocache(res->start, res_size); + dpctl->dp_size = res_size; + } + dpld->dp_base = dpctl->dp_base; + dpld->dp_size = dpctl->dp_size; + + mif_info("%s: dp_type %d, aligned %d, dp_base 0x%08X, dp_size %d\n", + ld->name, dpld->dp_type, ld->aligned, (int)dpld->dp_base, + dpld->dp_size); + + /* Initialize DPRAM map (physical map -> logical map) */ + ret = dpram_table_init(dpld); + if (ret < 0) { + mif_info("%s: ERR! dpram_table_init fail (err %d)\n", + ld->name, ret); + goto err; + } + + spin_lock_init(&dpld->pld_lock); + + /* Disable IPC */ + set_magic(dpld, 0); + set_access(dpld, 0); + + dpld->dpram_init_status = DPRAM_INIT_STATE_NONE; + + /* Initialize locks, completions, and bottom halves */ + snprintf(dpld->wlock_name, DP_MAX_NAME_LEN, "%s_wlock", ld->name); + wake_lock_init(&dpld->wlock, WAKE_LOCK_SUSPEND, dpld->wlock_name); + + init_completion(&dpld->dpram_init_cmd); + init_completion(&dpld->modem_pif_init_done); + init_completion(&dpld->udl_start_complete); + init_completion(&dpld->udl_cmd_complete); + init_completion(&dpld->dump_start_complete); + init_completion(&dpld->dump_recv_done); + + task_data = (unsigned long)dpld; + tasklet_init(&dpld->rx_tsk, dpram_ipc_rx_task, task_data); + + /* Prepare SKB queue head for RX processing */ + for (i = 0; i < dpld->max_ipc_dev; i++) + skb_queue_head_init(&dpld->skb_rxq[i]); + + /* Prepare RXB queue */ + qsize = DPRAM_MAX_RXBQ_SIZE; + for (i = 0; i < dpld->max_ipc_dev; i++) { + bsize = rxbq_get_page_size(get_rx_buff_size(dpld, i)); + dpld->rxbq[i].size = qsize; + dpld->rxbq[i].in = 0; + dpld->rxbq[i].out = 0; + dpld->rxbq[i].rxb = rxbq_create_pool(bsize, qsize); + if (!dpld->rxbq[i].rxb) { + mif_info("%s: ERR! %s rxbq_create_pool fail\n", + ld->name, get_dev_name(i)); + goto err; + } + mif_info("%s: %s rxbq_pool created (bsize:%d, qsize:%d)\n", + ld->name, get_dev_name(i), bsize, qsize); + } + + /* Prepare a multi-purpose miscellaneous buffer */ + dpld->buff = kzalloc(dpld->dp_size, GFP_KERNEL); + if (!dpld->buff) { + mif_info("%s: ERR! kzalloc dpld->buff fail\n", ld->name); + goto err; + } + + /* Retrieve DPRAM IRQ GPIO# */ + dpld->gpio_dpram_int = mdm_data->gpio_dpram_int; + + /* Retrieve DPRAM IRQ# */ + if (!dpctl->dpram_irq) { + dpctl->dpram_irq = platform_get_irq_byname(pdev, "dpram_irq"); + if (dpctl->dpram_irq < 0) { + mif_info("%s: ERR! platform_get_irq_byname fail\n", + ld->name); + goto err; + } + } + dpld->irq = dpctl->dpram_irq; + + /* Retrieve DPRAM IRQ flags */ + if (!dpctl->dpram_irq_flags) + dpctl->dpram_irq_flags = (IRQF_NO_SUSPEND | IRQF_TRIGGER_LOW); + dpld->irq_flags = dpctl->dpram_irq_flags; + + /* Register DPRAM interrupt handler */ + snprintf(dpld->irq_name, DP_MAX_NAME_LEN, "%s_irq", ld->name); + ret = dpram_register_isr(dpld->irq, dpram_irq_handler, dpld->irq_flags, + dpld->irq_name, dpld); + if (ret) + goto err; + + return ld; + +err: + if (dpld) { + kfree(dpld->buff); + kfree(dpld); + } + + return NULL; +} + diff --git a/drivers/misc/modem_if/modem_link_device_pld.h b/drivers/misc/modem_if/modem_link_device_pld.h new file mode 100644 index 0000000..2656110 --- /dev/null +++ b/drivers/misc/modem_if/modem_link_device_pld.h @@ -0,0 +1,532 @@ +/* + * Copyright (C) 2011 Google, Inc. + * Copyright (C) 2010 Samsung Electronics. + * + * 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. + * + */ +#ifndef __MODEM_LINK_DEVICE_DPRAM_H__ +#define __MODEM_LINK_DEVICE_DPRAM_H__ + +#include <linux/spinlock.h> +#include <linux/wakelock.h> +#include <linux/workqueue.h> +#include <linux/timer.h> +#include <linux/platform_data/modem.h> + +#include "modem_prj.h" + +#define DPRAM_MAGIC_CODE 0xAA + +/* interrupt masks.*/ +#define INT_MASK_VALID 0x0080 +#define INT_MASK_CMD 0x0040 +#define INT_VALID(x) ((x) & INT_MASK_VALID) +#define INT_CMD_VALID(x) ((x) & INT_MASK_CMD) +#define INT_NON_CMD(x) (INT_MASK_VALID | (x)) +#define INT_CMD(x) (INT_MASK_VALID | INT_MASK_CMD | (x)) + +#define EXT_UDL_MASK 0xF000 +#define EXT_UDL_CMD(x) ((x) & EXT_UDL_MASK) +#define EXT_INT_VALID_MASK 0x8000 +#define EXT_CMD_VALID_MASK 0x4000 +#define UDL_CMD_VALID_MASK 0x2000 +#define EXT_INT_VALID(x) ((x) & EXT_INT_VALID_MASK) +#define EXT_CMD_VALID(x) ((x) & EXT_CMD_VALID_MASK) +#define UDL_CMD_VALID(x) ((x) & UDL_CMD_VALID_MASK) +#define INT_EXT_CMD(x) (EXT_INT_VALID_MASK | EXT_CMD_VALID_MASK | (x)) + +#define EXT_CMD_MASK(x) ((x) & 0x0FFF) +#define EXT_CMD_SET_SPEED_LOW 0x0011 +#define EXT_CMD_SET_SPEED_MID 0x0012 +#define EXT_CMD_SET_SPEED_HIGH 0x0013 + +#define UDL_RESULT_SUCCESS 0x1 +#define UDL_RESULT_FAIL 0x2 + +#define UDL_CMD_MASK(x) (((x) >> 8) & 0xF) +#define UDL_CMD_RECV_READY 0x1 +#define UDL_CMD_DL_START_REQ 0x2 +#define UDL_CMD_DL_START_RESP 0x3 +#define UDL_CMD_IMAGE_SEND_REQ 0x4 +#define UDL_CMD_SEND_DONE_RESP 0x5 +#define UDL_CMD_SEND_DONE_REQ 0x6 +#define UDL_CMD_UPDATE_DONE 0x7 +#define UDL_CMD_STATUS_UPDATE 0x8 +#define UDL_CMD_IMAGE_SEND_RESP 0x9 +#define UDL_CMD_EFS_CLEAR_RESP 0xB +#define UDL_CMD_ALARM_BOOT_OK 0xC +#define UDL_CMD_ALARM_BOOT_FAIL 0xD + +#define CMD_IMG_START_REQ 0x9200 +#define CMD_IMG_SEND_REQ 0x9400 +#define CMD_DL_SEND_DONE_REQ 0x9600 +#define CMD_UL_RECV_RESP 0x9601 +#define CMD_UL_RECV_DONE_RESP 0x9801 + +/* special interrupt cmd indicating modem boot failure. */ +#define INT_POWERSAFE_FAIL 0xDEAD + +#define INT_MASK_REQ_ACK_RFS 0x0400 /* Request RES_ACK_RFS */ +#define INT_MASK_RES_ACK_RFS 0x0200 /* Response of REQ_ACK_RFS */ +#define INT_MASK_SEND_RFS 0x0100 /* Indicate sending RFS data */ + +#define INT_MASK_REQ_ACK_F 0x0020 +#define INT_MASK_REQ_ACK_R 0x0010 +#define INT_MASK_RES_ACK_F 0x0008 +#define INT_MASK_RES_ACK_R 0x0004 +#define INT_MASK_SEND_F 0x0002 +#define INT_MASK_SEND_R 0x0001 + +#define INT_MASK_REQ_ACK_RFS 0x0400 /* Request RES_ACK_RFS */ +#define INT_MASK_RES_ACK_RFS 0x0200 /* Response of REQ_ACK_RFS */ +#define INT_MASK_SEND_RFS 0x0100 /* Indicate sending RFS data */ + +#define INT_MASK_RES_ACK_SET \ + (INT_MASK_RES_ACK_F | INT_MASK_RES_ACK_R | INT_MASK_RES_ACK_RFS) + +#define INT_MASK_SEND_SET \ + (INT_MASK_SEND_F | INT_MASK_SEND_R | INT_MASK_SEND_RFS) + +#define INT_CMD_MASK(x) ((x) & 0xF) +#define INT_CMD_INIT_START 0x1 +#define INT_CMD_INIT_END 0x2 +#define INT_CMD_REQ_ACTIVE 0x3 +#define INT_CMD_RES_ACTIVE 0x4 +#define INT_CMD_REQ_TIME_SYNC 0x5 +#define INT_CMD_CRASH_RESET 0x7 +#define INT_CMD_PHONE_START 0x8 +#define INT_CMD_ERR_DISPLAY 0x9 +#define INT_CMD_CRASH_EXIT 0x9 +#define INT_CMD_CP_DEEP_SLEEP 0xA +#define INT_CMD_NV_REBUILDING 0xB +#define INT_CMD_EMER_DOWN 0xC +#define INT_CMD_PIF_INIT_DONE 0xD +#define INT_CMD_SILENT_NV_REBUILDING 0xE +#define INT_CMD_NORMAL_PWR_OFF 0xF + +#define START_FLAG 0x7F +#define END_FLAG 0x7E + +#define DP_MAGIC_DMDL 0x4445444C +#define DP_MAGIC_UMDL 0x4445444D +#define DP_DPRAM_SIZE 0x4000 +#define DP_DEFAULT_WRITE_LEN 8168 +#define DP_DEFAULT_DUMP_LEN 16128 +#define DP_DUMP_HEADER_SIZE 7 + +#define UDL_TIMEOUT (50 * HZ) +#define UDL_SEND_TIMEOUT (200 * HZ) +#define FORCE_CRASH_ACK_TIMEOUT (5 * HZ) +#define DUMP_TIMEOUT (30 * HZ) +#define DUMP_START_TIMEOUT (100 * HZ) +#define DUMP_WAIT_TIMEOUT (HZ >> 10) /* 1/1024 second */ + +#define PLD_ADDR_MASK(x) (0x00003FFF & (unsigned long)(x)) + +enum host_boot_mode { + HOST_BOOT_MODE_NORMAL, + HOST_BOOT_MODE_DUMP, +}; + +enum dpram_init_status { + DPRAM_INIT_STATE_NONE, + DPRAM_INIT_STATE_READY, +}; + +struct dpram_boot_img { + char *addr; + int size; + enum host_boot_mode mode; + unsigned req; + unsigned resp; +}; + +#define MAX_PAYLOAD_SIZE 0x2000 +struct dpram_boot_frame { + unsigned req; /* AP->CP request */ + unsigned resp; /* response expected by AP */ + ssize_t len; /* data size in the buffer */ + unsigned offset; /* offset to write into DPRAM */ + char data[MAX_PAYLOAD_SIZE]; +}; + +/* buffer type for modem image */ +struct dpram_dump_arg { + char *buff; /* pointer to the buffer */ + int buff_size; /* buffer size */ + unsigned req; /* AP->CP request */ + unsigned resp; /* CP->AP response */ + bool cmd; /* AP->CP command */ +}; + +struct dpram_firmware { + char *firmware; + int size; + int is_delta; +}; +enum dpram_link_mode { + DPRAM_LINK_MODE_INVALID = 0, + DPRAM_LINK_MODE_IPC, + DPRAM_LINK_MODE_BOOT, + DPRAM_LINK_MODE_DLOAD, + DPRAM_LINK_MODE_ULOAD, +}; + +struct dpram_boot_map { + u32 __iomem *magic; + u8 __iomem *buff; + u32 __iomem *req; + u32 __iomem *resp; + u32 size; +}; + +struct qc_dpram_boot_map { + u8 __iomem *buff; + u16 __iomem *frame_size; + u16 __iomem *tag; + u16 __iomem *count; +}; + +struct dpram_dload_map { + u32 __iomem *magic; + u8 __iomem *buff; +}; + +struct dpram_uload_map { + u32 __iomem *magic; + u8 __iomem *buff; +}; + +struct dpram_ota_header { + u8 start_index; + u16 nframes; + u16 curframe; + u16 len; + +} __packed; + +struct ul_header { + u8 bop; + u16 total_frame; + u16 curr_frame; + u16 len; +} __packed; + +struct dpram_udl_param { + unsigned char *addr; + unsigned int size; + unsigned int count; + unsigned int tag; +}; + +struct dpram_udl_check { + unsigned int total_size; + unsigned int rest_size; + unsigned int send_size; + unsigned int copy_start; + unsigned int copy_complete; + unsigned int boot_complete; +}; + +#define DP_BOOT_BUFF_OFFSET 4 +#define DP_DLOAD_BUFF_OFFSET 4 +#define DP_ULOAD_BUFF_OFFSET 4 +#define DP_BOOT_REQ_OFFSET 0 +#define DP_BOOT_RESP_OFFSET 8 + +#define MAX_WQ_NAME_LENGTH 64 + +#define DPRAM_MAX_RXBQ_SIZE 256 + +struct dpram_rxb { + u8 *buff; + unsigned size; + + u8 *data; + unsigned len; +}; + +struct dpram_rxb_queue { + int size; + int in; + int out; + struct dpram_rxb *rxb; +}; + +/* + mbx_ap2cp + 0x0 + magic_code + + access_enable + + padding + + mbx_cp2ap + 0x1000 + magic_code + + access_enable + + padding + + fmt_tx_head + fmt_tx_tail + fmt_tx_buff + 0x2000 + raw_tx_head + raw_tx_tail + raw_tx_buff + + fmt_rx_head + fmt_rx_tail + fmt_rx_buff + 0x3000 + raw_rx_head + raw_rx_tail + raw_rx_buff + + = 2 + + 4094 + + 2 + + 4094 + + 2 + + 2 + + 2 + 2 + 1020 + + 2 + 2 + 3064 + + 2 + 2 + 1020 + + 2 + 2 + 3064 + */ + +#define DP_PLD_FMT_TX_BUFF_SZ 1024 +#define DP_PLD_RAW_TX_BUFF_SZ 3072 +#define DP_PLD_FMT_RX_BUFF_SZ 1024 +#define DP_PLD_RAW_RX_BUFF_SZ 3072 + +#define MAX_MSM_EDPRAM_IPC_DEV 2 /* FMT, RAW */ + +struct dpram_ipc_pld_map { + u16 mbx_ap2cp; + u16 magic_ap2cp; + u16 access_ap2cp; + u16 fmt_tx_head; + u16 raw_tx_head; + u16 fmt_rx_tail; + u16 raw_rx_tail; + u16 temp1; + u8 padding1[4080]; + + u16 mbx_cp2ap; + u16 magic_cp2ap; + u16 access_cp2ap; + u16 fmt_tx_tail; + u16 raw_tx_tail; + u16 fmt_rx_head; + u16 raw_rx_head; + u16 temp2; + u8 padding2[4080]; + + u8 fmt_tx_buff[DP_PLD_FMT_TX_BUFF_SZ]; + u8 raw_tx_buff[DP_PLD_RAW_TX_BUFF_SZ]; + u8 fmt_rx_buff[DP_PLD_RAW_TX_BUFF_SZ]; + u8 raw_rx_buff[DP_PLD_RAW_RX_BUFF_SZ]; + + u8 padding3[16384]; + + u16 address_buffer; +}; + +/* + magic_code + + access_enable + + fmt_tx_head + fmt_tx_tail + fmt_tx_buff + + raw_tx_head + raw_tx_tail + raw_tx_buff + + fmt_rx_head + fmt_rx_tail + fmt_rx_buff + + raw_rx_head + raw_rx_tail + raw_rx_buff + + mbx_cp2ap + + mbx_ap2cp + = 2 + + 2 + + 2 + 2 + 1336 + + 2 + 2 + 4564 + + 2 + 2 + 1336 + + 2 + 2 + 9124 + + 2 + + 2 + = 16384 +*/ +#define DP_16K_FMT_TX_BUFF_SZ 1336 +#define DP_16K_RAW_TX_BUFF_SZ 4564 +#define DP_16K_FMT_RX_BUFF_SZ 1336 +#define DP_16K_RAW_RX_BUFF_SZ 9124 + +struct dpram_ipc_16k_map { + u16 magic; + u16 access; + + u16 fmt_tx_head; + u16 fmt_tx_tail; + u8 fmt_tx_buff[DP_16K_FMT_TX_BUFF_SZ]; + + u16 raw_tx_head; + u16 raw_tx_tail; + u8 raw_tx_buff[DP_16K_RAW_TX_BUFF_SZ]; + + u16 fmt_rx_head; + u16 fmt_rx_tail; + u8 fmt_rx_buff[DP_16K_FMT_RX_BUFF_SZ]; + + u16 raw_rx_head; + u16 raw_rx_tail; + u8 raw_rx_buff[DP_16K_RAW_RX_BUFF_SZ]; + + u16 mbx_cp2ap; + u16 mbx_ap2cp; +}; + +#define DP_MAX_NAME_LEN 32 + +struct dpram_ext_op; + +struct dpram_link_device { + struct link_device ld; + + /* The mode of this DPRAM link device */ + enum dpram_link_mode mode; + + /* DPRAM address and size */ + u8 __iomem *dp_base; /* DPRAM base virtual address */ + u32 dp_size; /* DPRAM size */ + enum dpram_type dp_type; /* DPRAM type */ + + /* DPRAM IRQ GPIO# */ + unsigned gpio_dpram_int; + + /* DPRAM IRQ from CP */ + int irq; + unsigned long irq_flags; + char irq_name[DP_MAX_NAME_LEN]; + + /* Link to DPRAM control functions dependent on each platform */ + int max_ipc_dev; + struct modemlink_dpram_control *dpctl; + + /* Physical configuration -> logical configuration */ + union { + struct dpram_boot_map bt_map; + struct qc_dpram_boot_map qc_bt_map; + }; + + struct dpram_dload_map dl_map; + struct dpram_uload_map ul_map; + + /* IPC device map */ + struct dpram_ipc_map ipc_map; + + /* Pointers (aliases) to IPC device map */ + u16 __iomem *magic_ap2cp; + u16 __iomem *access_ap2cp; + u16 __iomem *magic_cp2ap; + u16 __iomem *access_cp2ap; + u16 __iomem *address_buffer; + + struct dpram_ipc_device *dev[MAX_IPC_DEV]; + u16 __iomem *mbx2ap; + u16 __iomem *mbx2cp; + + /* Wakelock for DPRAM device */ + struct wake_lock wlock; + char wlock_name[DP_MAX_NAME_LEN]; + + /* For booting */ + unsigned boot_start_complete; + struct completion dpram_init_cmd; + struct completion modem_pif_init_done; + + /* For UDL */ + struct tasklet_struct ul_tsk; + struct tasklet_struct dl_tsk; + struct completion udl_start_complete; + struct completion udl_cmd_complete; + struct dpram_udl_check udl_check; + struct dpram_udl_param udl_param; + + /* For CP RAM dump */ + struct timer_list crash_ack_timer; + struct completion dump_start_complete; + struct completion dump_recv_done; + struct timer_list dump_timer; + int dump_rcvd; /* Count of dump packets received */ + + /* For locking TX process */ + spinlock_t tx_rx_lock; + spinlock_t pld_lock; + + /* For efficient RX process */ + struct tasklet_struct rx_tsk; + struct dpram_rxb_queue rxbq[MAX_IPC_DEV]; + struct io_device *iod[MAX_IPC_DEV]; + bool use_skb; + struct sk_buff_head skb_rxq[MAX_IPC_DEV]; + + /* For retransmission after buffer full state */ + atomic_t res_required[MAX_IPC_DEV]; + + /* For wake-up/sleep control */ + atomic_t accessing; + + /* Multi-purpose miscellaneous buffer */ + u8 *buff; + + /* DPRAM IPC initialization status */ + int dpram_init_status; + + /* Alias to device-specific IOCTL function */ + int (*ext_ioctl)(struct dpram_link_device *dpld, struct io_device *iod, + unsigned int cmd, unsigned long arg); + + /* For DPRAM dump */ + void (*dpram_dump)(struct link_device *ld, char *buff); + + /* Common operations for each DPRAM */ + void (*clear_intr)(struct dpram_link_device *dpld); + u16 (*recv_intr)(struct dpram_link_device *dpld); + void (*send_intr)(struct dpram_link_device *dpld, u16 mask); + u16 (*get_magic)(struct dpram_link_device *dpld); + void (*set_magic)(struct dpram_link_device *dpld, u16 value); + u16 (*get_access)(struct dpram_link_device *dpld); + void (*set_access)(struct dpram_link_device *dpld, u16 value); + u32 (*get_tx_head)(struct dpram_link_device *dpld, int id); + u32 (*get_tx_tail)(struct dpram_link_device *dpld, int id); + void (*set_tx_head)(struct dpram_link_device *dpld, int id, u32 head); + void (*set_tx_tail)(struct dpram_link_device *dpld, int id, u32 tail); + u8 *(*get_tx_buff)(struct dpram_link_device *dpld, int id); + u32 (*get_tx_buff_size)(struct dpram_link_device *dpld, int id); + u32 (*get_rx_head)(struct dpram_link_device *dpld, int id); + u32 (*get_rx_tail)(struct dpram_link_device *dpld, int id); + void (*set_rx_head)(struct dpram_link_device *dpld, int id, u32 head); + void (*set_rx_tail)(struct dpram_link_device *dpld, int id, u32 tail); + u8 *(*get_rx_buff)(struct dpram_link_device *dpld, int id); + u32 (*get_rx_buff_size)(struct dpram_link_device *dpld, int id); + u16 (*get_mask_req_ack)(struct dpram_link_device *dpld, int id); + u16 (*get_mask_res_ack)(struct dpram_link_device *dpld, int id); + u16 (*get_mask_send)(struct dpram_link_device *dpld, int id); + + /* Extended operations for various modems */ + struct dpram_ext_op *ext_op; +}; + +/* converts from struct link_device* to struct xxx_link_device* */ +#define to_dpram_link_device(linkdev) \ + container_of(linkdev, struct dpram_link_device, ld) + +struct dpram_ext_op { + int exist; + + void (*init_boot_map)(struct dpram_link_device *dpld); + void (*init_dl_map)(struct dpram_link_device *dpld); + void (*init_ul_map)(struct dpram_link_device *dpld); + + int (*dload_bin)(struct dpram_link_device *dpld, struct sk_buff *skb); + void (*dload_cmd_handler)(struct dpram_link_device *dpld, u16 cmd); + + void (*cp_start_handler)(struct dpram_link_device *dpld); + + void (*crash_log)(struct dpram_link_device *dpld); + int (*dump_start)(struct dpram_link_device *dpld); + int (*dump_update)(struct dpram_link_device *dpld, void *arg); + + int (*ioctl)(struct dpram_link_device *dpld, struct io_device *iod, + unsigned int cmd, unsigned long arg); +}; + +struct dpram_ext_op *dpram_get_ext_op(enum modem_t modem); +#endif diff --git a/drivers/misc/modem_if/modem_link_device_pld_ext_op.c b/drivers/misc/modem_if/modem_link_device_pld_ext_op.c new file mode 100644 index 0000000..ae6578c --- /dev/null +++ b/drivers/misc/modem_if/modem_link_device_pld_ext_op.c @@ -0,0 +1,556 @@ +/* + * Copyright (C) 2010 Samsung Electronics. + * + * 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/irq.h> +#include <linux/gpio.h> +#include <linux/time.h> +#include <linux/interrupt.h> +#include <linux/timer.h> +#include <linux/wakelock.h> +#include <linux/delay.h> +#include <linux/wait.h> +#include <linux/sched.h> +#include <linux/vmalloc.h> +#include <linux/if_arp.h> +#include <linux/platform_device.h> +#include <linux/kallsyms.h> +#include <linux/platform_data/modem.h> + +#include "modem_prj.h" +#include "modem_link_device_pld.h" +#include "modem_utils.h" + +#if defined(CONFIG_CDMA_MODEM_MDM6600) || defined(CONFIG_GSM_MODEM_ESC6270) +enum qc_dload_tag { + QC_DLOAD_TAG_NONE = 0, + QC_DLOAD_TAG_BIN, + QC_DLOAD_TAG_NV, + QC_DLOAD_TAG_MAX +}; + +static void qc_dload_task(unsigned long data); + +static void qc_init_boot_map(struct dpram_link_device *dpld) +{ + struct qc_dpram_boot_map *bt_map = &dpld->qc_bt_map; + struct modemlink_dpram_control *dpctl = dpld->dpctl; + + bt_map->buff = dpld->dev[0]->txq.buff; + bt_map->frame_size = (u16 *)(dpld->dp_base + dpctl->boot_size_offset); + bt_map->tag = (u16 *)(dpld->dp_base + dpctl->boot_tag_offset); + bt_map->count = (u16 *)(dpld->dp_base + dpctl->boot_count_offset); + + tasklet_init(&dpld->dl_tsk, qc_dload_task, (unsigned long)dpld); +} + +static void qc_dload_map(struct dpram_link_device *dpld, u8 is_upload) +{ + struct qc_dpram_boot_map *bt_map = &dpld->qc_bt_map; + struct modemlink_dpram_control *dpctl = dpld->dpctl; + unsigned int upload_offset = 0; + + if (is_upload == 1) { + upload_offset = 0x1000; + bt_map->buff = dpld->dev[0]->rxq.buff; + } else { + upload_offset = 0; + bt_map->buff = dpld->dev[0]->txq.buff; + } + + bt_map->frame_size = (u16 *)(dpld->dp_base + + dpctl->boot_size_offset + upload_offset); + bt_map->tag = (u16 *)(dpld->dp_base + + dpctl->boot_tag_offset + upload_offset); + bt_map->count = (u16 *)(dpld->dp_base + + dpctl->boot_count_offset + upload_offset); + +} + +static int qc_prepare_download(struct dpram_link_device *dpld) +{ + int retval = 0; + int count = 0; + + qc_dload_map(dpld, 0); + + while (1) { + if (dpld->udl_check.copy_start) { + dpld->udl_check.copy_start = 0; + break; + } + + msleep(20); + + count++; + if (count > 1000) { + mif_err("ERR! count %d\n", count); + return -1; + } + } + + return retval; +} + +static void _qc_do_download(struct dpram_link_device *dpld, + struct dpram_udl_param *param) +{ + struct qc_dpram_boot_map *bt_map = &dpld->qc_bt_map; + + if (param->size <= dpld->dpctl->max_boot_frame_size) { + iowrite16(PLD_ADDR_MASK(&bt_map->buff[0]), + dpld->address_buffer); + memcpy(dpld->dp_base, param->addr, param->size); + + iowrite16(PLD_ADDR_MASK(&bt_map->frame_size[0]), + dpld->address_buffer); + iowrite16(param->size, dpld->dp_base); + + iowrite16(PLD_ADDR_MASK(&bt_map->tag[0]), + dpld->address_buffer); + iowrite16(param->tag, dpld->dp_base); + + iowrite16(PLD_ADDR_MASK(&bt_map->count[0]), + dpld->address_buffer); + iowrite16(param->count, dpld->dp_base); + + dpld->send_intr(dpld, 0xDB12); + } else { + mif_info("param->size %d\n", param->size); + } +} + +static int _qc_download(struct dpram_link_device *dpld, void *arg, + enum qc_dload_tag tag) +{ + int retval = 0; + int count = 0; + int cnt_limit; + unsigned char *img; + struct dpram_udl_param param; + + retval = copy_from_user((void *)¶m, (void *)arg, sizeof(param)); + if (retval < 0) { + mif_err("ERR! copy_from_user fail\n"); + return -1; + } + + img = vmalloc(param.size); + if (!img) { + mif_err("ERR! vmalloc fail\n"); + return -1; + } + memset(img, 0, param.size); + memcpy(img, param.addr, param.size); + + dpld->udl_check.total_size = param.size; + dpld->udl_check.rest_size = param.size; + dpld->udl_check.send_size = 0; + dpld->udl_check.copy_complete = 0; + + dpld->udl_param.addr = img; + dpld->udl_param.size = dpld->dpctl->max_boot_frame_size; + if (tag == QC_DLOAD_TAG_NV) + dpld->udl_param.count = 1; + else + dpld->udl_param.count = param.count; + dpld->udl_param.tag = tag; + + if (dpld->udl_check.rest_size < dpld->dpctl->max_boot_frame_size) + dpld->udl_param.size = dpld->udl_check.rest_size; + + /* Download image (binary or NV) */ + _qc_do_download(dpld, &dpld->udl_param); + + /* Wait for completion + */ + if (tag == QC_DLOAD_TAG_NV) + cnt_limit = 200; + else + cnt_limit = 1000; + + while (1) { + if (dpld->udl_check.copy_complete) { + dpld->udl_check.copy_complete = 0; + retval = 0; + break; + } + + msleep(20); + + count++; + if (count > cnt_limit) { + mif_err("ERR! count %d\n", count); + retval = -1; + break; + } + } + + vfree(img); + + return retval; +} + +static int qc_download_bin(struct dpram_link_device *dpld, void *arg) +{ + return _qc_download(dpld, arg, QC_DLOAD_TAG_BIN); +} + +static int qc_download_nv(struct dpram_link_device *dpld, void *arg) +{ + return _qc_download(dpld, arg, QC_DLOAD_TAG_NV); +} + +static void qc_dload_task(unsigned long data) +{ + struct dpram_link_device *dpld = (struct dpram_link_device *)data; + + dpld->udl_check.send_size += dpld->udl_param.size; + dpld->udl_check.rest_size -= dpld->udl_param.size; + + dpld->udl_param.addr += dpld->udl_param.size; + + if (dpld->udl_check.send_size >= dpld->udl_check.total_size) { + dpld->udl_check.copy_complete = 1; + dpld->udl_param.tag = 0; + return; + } + + if (dpld->udl_check.rest_size < dpld->dpctl->max_boot_frame_size) + dpld->udl_param.size = dpld->udl_check.rest_size; + + dpld->udl_param.count += 1; + + _qc_do_download(dpld, &dpld->udl_param); +} + +static void qc_dload_cmd_handler(struct dpram_link_device *dpld, u16 cmd) +{ + switch (cmd) { + case 0x1234: + dpld->udl_check.copy_start = 1; + break; + + case 0xDBAB: + tasklet_schedule(&dpld->dl_tsk); + break; + + case 0xABCD: + mif_info("[%s] booting Start\n", dpld->ld.name); + dpld->udl_check.boot_complete = 1; + break; + + default: + mif_err("ERR! unknown command 0x%04X\n", cmd); + } +} + +static int qc_boot_start(struct dpram_link_device *dpld) +{ + u16 mask = 0; + int count = 0; + + /* Send interrupt -> '0x4567' */ + mask = 0x4567; + dpld->send_intr(dpld, mask); + + while (1) { + if (dpld->udl_check.boot_complete) { + dpld->udl_check.boot_complete = 0; + break; + } + + msleep(20); + + count++; + if (count > 200) { + mif_err("ERR! count %d\n", count); + return -1; + } + } + + return 0; +} + +static int qc_boot_post_process(struct dpram_link_device *dpld) +{ + int count = 0; + + while (1) { + if (dpld->boot_start_complete) { + dpld->boot_start_complete = 0; + break; + } + + msleep(20); + + count++; + if (count > 200) { + mif_err("ERR! count %d\n", count); + return -1; + } + } + + return 0; +} + +static void qc_start_handler(struct dpram_link_device *dpld) +{ + /* + * INT_MASK_VALID | INT_MASK_CMD | INT_MASK_CP_AIRPLANE_BOOT | + * INT_MASK_CP_AP_ANDROID | INT_MASK_CMD_INIT_END + */ + u16 mask = (0x0080 | 0x0040 | 0x1000 | 0x0100 | 0x0002); + + dpld->boot_start_complete = 1; + + /* Send INIT_END code to CP */ + mif_info("send 0x%04X (INIT_END)\n", mask); + + dpld->send_intr(dpld, mask); +} + +static void qc_crash_log(struct dpram_link_device *dpld) +{ + struct link_device *ld = &dpld->ld; + static unsigned char buf[151]; + u8 __iomem *data = NULL; + + data = dpld->get_rx_buff(dpld, IPC_FMT); + memcpy(buf, data, (sizeof(buf) - 1)); + + mif_info("PHONE ERR MSG\t| %s Crash\n", ld->mdm_data->name); + mif_info("PHONE ERR MSG\t| %s\n", buf); +} + +static int _qc_data_upload(struct dpram_link_device *dpld, + struct dpram_udl_param *param) +{ + struct qc_dpram_boot_map *bt_map = &dpld->qc_bt_map; + int retval = 0; + u16 intval = 0; + int count = 0; + + while (1) { + if (!gpio_get_value(dpld->gpio_dpram_int)) { + intval = dpld->recv_intr(dpld); + if (intval == 0xDBAB) { + break; + } else { + mif_err("intr 0x%08x\n", intval); + return -1; + } + } + + msleep(20); + + count++; + if (count > 200) { + mif_err("<%s:%d>\n", __func__, __LINE__); + return -1; + } + } + + iowrite16(PLD_ADDR_MASK(&bt_map->frame_size[0]), + dpld->address_buffer); + param->size = ioread16(dpld->dp_base); + + iowrite16(PLD_ADDR_MASK(&bt_map->tag[0]), + dpld->address_buffer); + param->tag = ioread16(dpld->dp_base); + + iowrite16(PLD_ADDR_MASK(&bt_map->count[0]), + dpld->address_buffer); + param->count = ioread16(dpld->dp_base); + + iowrite16(PLD_ADDR_MASK(&bt_map->buff[0]), + dpld->address_buffer); + memcpy(param->addr, dpld->dp_base, param->size); + + dpld->send_intr(dpld, 0xDB12); + + return retval; +} + +static int qc_uload_step1(struct dpram_link_device *dpld) +{ + int retval = 0; + int count = 0; + u16 intval = 0; + u16 mask = 0; + + qc_dload_map(dpld, 1); + + mif_info("+---------------------------------------------+\n"); + mif_info("| UPLOAD PHONE SDRAM |\n"); + mif_info("+---------------------------------------------+\n"); + + while (1) { + if (!gpio_get_value(dpld->gpio_dpram_int)) { + intval = dpld->recv_intr(dpld); + mif_info("intr 0x%04x\n", intval); + if (intval == 0x1234) { + break; + } else { + mif_info("ERR! invalid intr\n"); + return -1; + } + } + + msleep(20); + + count++; + if (count > 200) { + intval = dpld->recv_intr(dpld); + mif_info("count %d, intr 0x%04x\n", count, intval); + if (intval == 0x1234) + break; + return -1; + } + } + + mask = 0xDEAD; + dpld->send_intr(dpld, mask); + + return retval; +} + +static int qc_uload_step2(struct dpram_link_device *dpld, void *arg) +{ + int retval = 0; + struct dpram_udl_param param; + + retval = copy_from_user((void *)¶m, (void *)arg, sizeof(param)); + if (retval < 0) { + mif_err("ERR! copy_from_user fail (err %d)\n", retval); + return -1; + } + + retval = _qc_data_upload(dpld, ¶m); + if (retval < 0) { + mif_err("ERR! _qc_data_upload fail (err %d)\n", retval); + return -1; + } + + if (!(param.count % 500)) + mif_info("param->count = %d\n", param.count); + + if (param.tag == 4) { + enable_irq(dpld->irq); + mif_info("param->tag = %d\n", param.tag); + } + + retval = copy_to_user((unsigned long *)arg, ¶m, sizeof(param)); + if (retval < 0) { + mif_err("ERR! copy_to_user fail (err %d)\n", retval); + return -1; + } + + return retval; +} + +static int qc_ioctl(struct dpram_link_device *dpld, struct io_device *iod, + unsigned int cmd, unsigned long arg) +{ + struct link_device *ld = &dpld->ld; + int err = 0; + + switch (cmd) { + case IOCTL_DPRAM_PHONE_POWON: + err = qc_prepare_download(dpld); + if (err < 0) + mif_info("%s: ERR! prepare_download fail\n", ld->name); + break; + + case IOCTL_DPRAM_PHONEIMG_LOAD: + err = qc_download_bin(dpld, (void *)arg); + if (err < 0) + mif_info("%s: ERR! download_bin fail\n", ld->name); + break; + + case IOCTL_DPRAM_NVDATA_LOAD: + err = qc_download_nv(dpld, (void *)arg); + if (err < 0) + mif_info("%s: ERR! download_nv fail\n", ld->name); + break; + + case IOCTL_DPRAM_PHONE_BOOTSTART: + err = qc_boot_start(dpld); + if (err < 0) { + mif_info("%s: ERR! boot_start fail\n", ld->name); + break; + } + + err = qc_boot_post_process(dpld); + if (err < 0) + mif_info("%s: ERR! boot_post_process fail\n", ld->name); + + break; + + case IOCTL_DPRAM_PHONE_UPLOAD_STEP1: + disable_irq_nosync(dpld->irq); + err = qc_uload_step1(dpld); + if (err < 0) { + enable_irq(dpld->irq); + mif_info("%s: ERR! upload_step1 fail\n", ld->name); + } + break; + + case IOCTL_DPRAM_PHONE_UPLOAD_STEP2: + err = qc_uload_step2(dpld, (void *)arg); + if (err < 0) { + enable_irq(dpld->irq); + mif_info("%s: ERR! upload_step2 fail\n", ld->name); + } + break; + + default: + mif_err("%s: ERR! invalid cmd 0x%08X\n", ld->name, cmd); + err = -EINVAL; + break; + } + + return err; +} +#endif + +static struct dpram_ext_op ext_op_set[] = { +#if defined(CONFIG_CDMA_MODEM_MDM6600) + [QC_MDM6600] = { + .exist = 1, + .init_boot_map = qc_init_boot_map, + .dload_cmd_handler = qc_dload_cmd_handler, + .cp_start_handler = qc_start_handler, + .crash_log = qc_crash_log, + .ioctl = qc_ioctl, + }, +#endif +#if defined(CONFIG_GSM_MODEM_ESC6270) + [QC_ESC6270] = { + .exist = 1, + .init_boot_map = qc_init_boot_map, + .dload_cmd_handler = qc_dload_cmd_handler, + .cp_start_handler = qc_start_handler, + .crash_log = qc_crash_log, + .ioctl = qc_ioctl, + }, +#endif +}; + +struct dpram_ext_op *dpram_get_ext_op(enum modem_t modem) +{ + if (ext_op_set[modem].exist) + return &ext_op_set[modem]; + else + return NULL; +} diff --git a/drivers/misc/modem_if/modem_link_device_spi.c b/drivers/misc/modem_if/modem_link_device_spi.c new file mode 100644 index 0000000..1ea5a2c --- /dev/null +++ b/drivers/misc/modem_if/modem_link_device_spi.c @@ -0,0 +1,1795 @@ +/* + * Copyright (C) 2011 Google, Inc. + * Copyright (C) 2010 Samsung Electronics. + * + * 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/irq.h> +#include <linux/gpio.h> +#include <linux/time.h> +#include <linux/interrupt.h> +#include <linux/timer.h> +#include <linux/wakelock.h> +#include <linux/delay.h> +#include <linux/wait.h> +#include <linux/sched.h> +#include <linux/vmalloc.h> +#include <linux/if_arp.h> +#include <linux/platform_device.h> +#include <linux/spi/spi.h> +#include <linux/kthread.h> + +#include <linux/platform_data/modem.h> +#include "modem_prj.h" +#include "modem_link_device_spi.h" +#include "modem_utils.h" + +/* For function which has void parmeter */ +static struct spi_link_device *p_spild; +static struct spi_device *p_spi; + +static void spi_send_work(int spi_sigs) +{ + struct spi_work_type *spi_wq = NULL; + spi_wq = kmalloc(sizeof(struct spi_work_type), GFP_ATOMIC); + spi_wq->signal_code = spi_sigs; + INIT_WORK(&spi_wq->work, spi_work); + queue_work(p_spild->spi_wq, (struct work_struct *)spi_wq); +} + +static void spi_send_work_front(int spi_sigs) +{ + struct spi_work_type *spi_wq = NULL; + spi_wq = kmalloc(sizeof(struct spi_work_type), GFP_ATOMIC); + spi_wq->signal_code = spi_sigs; + INIT_WORK(&spi_wq->work, spi_work); + queue_work_front(p_spild->spi_wq, (struct work_struct *)spi_wq); +} + +static irqreturn_t spi_srdy_irq_handler(int irq, void *p_ld) +{ + struct link_device *ld = (struct link_device *)p_ld; + struct spi_link_device *spild = to_spi_link_device(ld); + + irqreturn_t result = IRQ_HANDLED; + + if (!spild->boot_done) + return result; + + if (!wake_lock_active(&spild->spi_wake_lock) + && spild->send_modem_spi != 1) { + wake_lock(&spild->spi_wake_lock); + pr_debug("[SPI] [%s](%d) spi_wakelock locked . spild->spi_state[%d]\n", + __func__, __LINE__, (int)spild->spi_state); + } + + if (spild->send_modem_spi == 1) + up(&spild->srdy_sem); + + /* SRDY interrupt work on SPI_STATE_IDLE state for receive data */ + if (spild->spi_state == SPI_STATE_IDLE + || spild->spi_state == SPI_STATE_RX_TERMINATE + || spild->spi_state == SPI_STATE_TX_TERMINATE) { + spi_send_work_front(SPI_WORK_RECEIVE); + + return result; + } + + return result; +} + +static irqreturn_t spi_subsrdy_irq_handler(int irq, void *p_ld) +{ + struct link_device *ld = (struct link_device *)p_ld; + struct spi_link_device *spild = to_spi_link_device(ld); + + irqreturn_t result = IRQ_HANDLED; + + /* SRDY interrupt work on SPI_STATE_TX_WAIT state for send data */ + if (spild->spi_state == SPI_STATE_TX_WAIT) + return result; + + pr_debug("%s spild->spi_state[%d]\n", + "[SPI] spi_main_subsrdy_rising_handler :", + (int)spild->spi_state); + + + return result; +} + +static int spi_send +( + struct link_device *ld, + struct io_device *iod, + struct sk_buff *skb +) +{ + struct sk_buff_head *txq; + enum dev_format fmt = iod->format; + + u32 data; + u32 cmd_ready = 0x12341234; + u32 cmd_start = 0x45674567; + int ret; + + switch (fmt) { + case IPC_FMT: + case IPC_RAW: + case IPC_RFS: + txq = ld->skb_txq[fmt]; + skb_queue_tail(txq, skb); + + ret = skb->len; + + break; + + case IPC_BOOT: + if (get_user(data, (u32 __user *)skb->data)) + return -EFAULT; + + if (data == cmd_ready) { + p_spild->ril_send_modem_img = 1; + p_spild->ril_send_cnt = 0; + } else if (data == cmd_start) { + p_spild->ril_send_modem_img = 0; + if (!queue_work(p_spild->ipc_spi_wq, + &p_spild->send_modem_w)) + pr_err("(%d) already exist w-q\n", + __LINE__); + } else { + if (p_spild->ril_send_modem_img) { + memcpy((void *)(p_spild->p_virtual_buff + + p_spild->ril_send_cnt), + skb->data, skb->len); + p_spild->ril_send_cnt += skb->len; + } + } + ret = skb->len; + dev_kfree_skb_any(skb); + + return ret; + + default: + pr_err("[LNK/E] <%s:%s> No TXQ for %s\n", + __func__, ld->name, iod->name); + dev_kfree_skb_any(skb); + return 0; + } + + spi_send_work(SPI_WORK_SEND); + + return ret; +} + + +static int spi_register_isr +( + unsigned irq, + irqreturn_t (*isr)(int, void*), + unsigned long flag, + const char *name, + struct link_device *ld +) +{ + int ret = 0; + + ret = request_irq(irq, isr, flag, name, ld); + if (ret) { + pr_err("[LNK/E] <%s> request_irq fail (%d)\n", + __func__, ret); + return ret; + } + + ret = enable_irq_wake(irq); + if (ret) + pr_err("[LNK/E] <%s> enable_irq_wake fail (%d)\n", + __func__, ret); + + pr_debug("[LNK] <%s> IRQ#%d handler is registered.\n", __func__, irq); + + return 0; +} + +void spi_unregister_isr(unsigned irq, void *data) +{ + free_irq(irq, data); +} + +int spi_tx_rx_sync(u8 *tx_d, u8 *rx_d, unsigned len) +{ + struct spi_transfer t; + struct spi_message msg; + + memset(&t, 0, sizeof t); + + t.len = len; + + t.tx_buf = tx_d; + t.rx_buf = rx_d; + + t.cs_change = 0; + + t.bits_per_word = 32; + t.speed_hz = 12000000; + + spi_message_init(&msg); + spi_message_add_tail(&t, &msg); + + return spi_sync(p_spi, &msg); +} + +static int spi_buff_write +( + struct spi_link_device *spild, + int dev_id, + const char *buff, + unsigned size +) +{ + u32 templength, buf_length; + u32 spi_data_mux; + u32 spi_packet_free_length; + + u8 *spi_packet; + struct spi_data_packet_header *spi_packet_header; + + spi_packet_header = (struct spi_data_packet_header *)spild->buff; + spi_packet = (char *)spild->buff; + + spi_packet_free_length = SPI_DATA_PACKET_MAX_PACKET_BODY_SIZE - + spi_packet_header->current_data_size; + + buf_length = size + SPI_DATA_MUX_SIZE + SPI_DATA_LENGTH_SIZE; + + /* not enough space in spi packet */ + if (spi_packet_free_length < buf_length) { + spi_packet_header->more = 1; + return 0; + } + + /* check spi mux type */ + switch (dev_id) { + case IPC_FMT: + spi_data_mux = SPI_DATA_MUX_IPC; + break; + + case IPC_RAW: + spi_data_mux = SPI_DATA_MUX_RAW; + break; + + case IPC_RFS: + spi_data_mux = SPI_DATA_MUX_RFS; + break; + + default: + pr_err("%s %s\n", + "[SPI] ERROR : spi_buff_write:", + "invalid dev_id"); + return 0; + } + + /* copy spi mux field */ + memcpy(spi_packet + SPI_DATA_PACKET_HEADER_SIZE + + spi_packet_header->current_data_size, + &spi_data_mux, SPI_DATA_MUX_SIZE); + spi_packet_header->current_data_size += SPI_DATA_MUX_SIZE; + + /* copy spi data length field */ + templength = size-SPI_DATA_BOF_SIZE-SPI_DATA_EOF_SIZE; + memcpy(spi_packet + SPI_DATA_PACKET_HEADER_SIZE + + spi_packet_header->current_data_size, + &templength, SPI_DATA_LENGTH_SIZE); + spi_packet_header->current_data_size += SPI_DATA_LENGTH_SIZE; + + /* copy data field */ + memcpy(spi_packet + SPI_DATA_PACKET_HEADER_SIZE + + spi_packet_header->current_data_size, + buff, size); + spi_packet_header->current_data_size += size; + + return buf_length; +} + + +int spi_prepare_tx_packet(void) +{ + struct link_device *ld; + struct spi_link_device *spild; + struct sk_buff *skb; + int ret; + int i; + + spild = p_spild; + ld = &spild->ld; + + for (i = 0; i < spild->max_ipc_dev; i++) { + while ((skb = skb_dequeue(ld->skb_txq[i]))) { + if (ld->mode == LINK_MODE_IPC) { + ret = spi_buff_write(spild, i, + skb->data, skb->len); + if (!ret) { + skb_queue_head(ld->skb_txq[i], skb); + break; + } + } else { + pr_err("[LNK/E] <%s:%s> " + "ld->mode != LINK_MODE_IPC\n", + __func__, ld->name); + } + dev_kfree_skb_any(skb); + } + } + + return 1; +} + + +static int spi_start_data_send(void) +{ + struct link_device *ld; + struct spi_link_device *spild; + int i; + + spild = p_spild; + ld = &spild->ld; + + for (i = 0; i < spild->max_ipc_dev; i++) { + if (skb_queue_len(ld->skb_txq[i]) > 0) { + spi_send_work(SPI_WORK_SEND); + return 1; + } + } + + return 0; +} + +static void spi_tx_work(void) +{ + struct spi_link_device *spild; + struct io_device *iod; + char *spi_packet_buf; + char *spi_sync_buf; + + spild = p_spild; + + /* check SUB SRDY state */ + if (gpio_get_value(spild->gpio_ipc_sub_srdy) == + SPI_GPIOLEVEL_HIGH) { + spi_start_data_send(); + return; + } + + /* check SRDY state */ + if (gpio_get_value(spild->gpio_ipc_srdy) == + SPI_GPIOLEVEL_HIGH) { + spi_start_data_send(); + return; + } + + if (get_console_suspended()) + return; + + if (spild->spi_state == SPI_STATE_END) + return; + + /* change state SPI_STATE_IDLE to SPI_STATE_TX_WAIT */ + spild->spi_state = SPI_STATE_TX_WAIT; + spild->spi_timer_tx_state = SPI_STATE_TIME_START; + + gpio_set_value(spild->gpio_ipc_mrdy, SPI_GPIOLEVEL_HIGH); + + /* Start TX timer */ + spild->spi_tx_timer.expires = + jiffies + ((SPI_TIMER_TX_WAIT_TIME * HZ) / 1000); + add_timer(&spild->spi_tx_timer); + /* check SUBSRDY state */ + while (gpio_get_value(spild->gpio_ipc_sub_srdy) == + SPI_GPIOLEVEL_LOW) { + if (spild->spi_timer_tx_state == SPI_STATE_TIME_OVER) { + pr_err("%s spild->spi_state=[%d]\n", + "[spi_tx_work] == spi Fail to receiving SUBSRDY CONF :" + , (int)spild->spi_state); + + spild->spi_timer_tx_state = SPI_STATE_TIME_START; + + gpio_set_value(spild->gpio_ipc_mrdy, + SPI_GPIOLEVEL_LOW); + + /* change state SPI_STATE_TX_WAIT */ + /* to SPI_STATE_IDLE */ + spild->spi_state = SPI_STATE_IDLE; + spi_send_work(SPI_WORK_SEND); + + return; + } + } + /* Stop TX timer */ + del_timer(&spild->spi_tx_timer); + + if (spild->spi_state != SPI_STATE_START + && spild->spi_state != SPI_STATE_END + && spild->spi_state != SPI_STATE_INIT) { + spi_packet_buf = spild->buff; + spi_sync_buf = spild->sync_buff; + + gpio_set_value(spild->gpio_ipc_sub_mrdy, SPI_GPIOLEVEL_HIGH); + gpio_set_value(spild->gpio_ipc_mrdy, SPI_GPIOLEVEL_LOW); + + /* change state SPI_STATE_TX_WAIT to + SPI_MAIN_STATE_TX_SENDING */ + spild->spi_state = SPI_STATE_TX_SENDING; + + memset(spi_packet_buf, 0, SPI_MAX_PACKET_SIZE); + memset(spi_sync_buf, 0, SPI_MAX_PACKET_SIZE); + + spi_prepare_tx_packet(); + + if (spi_tx_rx_sync((void *)spi_packet_buf, + (void *)spi_sync_buf, + SPI_MAX_PACKET_SIZE) != 0) { + /* TODO: save failed packet */ + /* back data to each queue */ + pr_err("[SPI] spi_dev_send fail\n"); + + /* add cp reset when spi sync fail */ + iod = link_get_iod_with_format(&spild->ld, IPC_FMT); + + if (iod) + iod->modem_state_changed(iod, + STATE_CRASH_RESET); + } + + spild->spi_state = SPI_STATE_TX_TERMINATE; + + gpio_set_value(spild->gpio_ipc_sub_mrdy, SPI_GPIOLEVEL_LOW); + + /* change state SPI_MAIN_STATE_TX_SENDING to SPI_STATE_IDLE */ + spild->spi_state = SPI_STATE_IDLE; + spi_start_data_send(); + } else + pr_err("[SPI] ERR : _start_packet_tx:spild->spi_state[%d]", + (int)spild->spi_state); + + return; +} + +int spi_buff_read(struct spi_link_device *spild) +{ + struct link_device *ld; + struct spi_data_packet_header *spi_packet_header; + struct sk_buff *skb; + char *spi_packet; + int dev_id; + unsigned int spi_packet_length; + unsigned int spi_packet_cur_pos = SPI_DATA_PACKET_HEADER_SIZE; + + unsigned int spi_data_mux; + unsigned int spi_data_length; + unsigned int data_length; + u8 *spi_cur_data; + u8 *dst; + + spi_packet = spild->buff; + ld = &spild->ld; + + /* check spi packet header */ + if (*(unsigned int *)spi_packet == 0x00000000 + || *(unsigned int *)spi_packet == 0xFFFFFFFF) { + /* if spi header is invalid, */ + /* read spi header again with next 4 byte */ + spi_packet += SPI_DATA_PACKET_HEADER_SIZE; + } + + /* read spi packet header */ + spi_packet_header = (struct spi_data_packet_header *)spi_packet; + spi_packet_length = SPI_DATA_PACKET_HEADER_SIZE + + spi_packet_header->current_data_size; + + + do { + /* read spi data mux and set current queue */ + memcpy(&spi_data_mux, + spi_packet + spi_packet_cur_pos, SPI_DATA_MUX_SIZE); + + switch (spi_data_mux & SPI_DATA_MUX_NORMAL_MASK) { + case SPI_DATA_MUX_IPC: + dev_id = IPC_FMT; + break; + + case SPI_DATA_MUX_RAW: + dev_id = IPC_RAW; + break; + + case SPI_DATA_MUX_RFS: + dev_id = IPC_RFS; + break; + + default: + pr_err("%s len[%u], pos[%u]\n", + "[SPI] ERROR : spi_buff_read : MUX error", + spi_packet_length, spi_packet_cur_pos); + + return spi_packet_cur_pos - + SPI_DATA_PACKET_HEADER_SIZE; + } + + /* read spi data length */ + memcpy(&spi_data_length, spi_packet + + spi_packet_cur_pos + SPI_DATA_LENGTH_OFFSET, + SPI_DATA_LENGTH_SIZE); + + data_length = spi_data_length + SPI_DATA_BOF_SIZE + + SPI_DATA_EOF_SIZE; + + spi_data_length += SPI_DATA_HEADER_SIZE; + + /* read data and make spi data */ + spi_cur_data = spi_packet + spi_packet_cur_pos; + + /* enqueue spi data */ + skb = alloc_skb(data_length, GFP_ATOMIC); + if (!skb) { + pr_err("%s %s\n", + "[SPI] ERROR : spi_buff_read:", + "Can't allocate memory for SPI"); + return 0; + } + + dst = skb_put(skb, data_length); + + memcpy(dst, spi_packet + + spi_packet_cur_pos + SPI_DATA_BOF_OFFSET, + data_length); + + if (skb) + skb_queue_tail(&spild->skb_rxq[dev_id], skb); + else + pr_err("[LNK/E] <%s:%s> read[%d] fail\n", + __func__, ld->name, dev_id); + + /* move spi packet current posision */ + spi_packet_cur_pos += spi_data_length; + } while ((spi_packet_length - 1) > spi_packet_cur_pos); + + return 1; +} + +static void spi_rx_work(void) +{ + struct link_device *ld; + struct spi_link_device *spild; + struct sk_buff *skb; + struct io_device *iod; + char *spi_packet_buf; + char *spi_sync_buf; + int i; + + spild = p_spild; + ld = &spild->ld; + if (!spild) + pr_err("[LNK/E] <%s> dpld == NULL\n", __func__); + + if (!wake_lock_active(&spild->spi_wake_lock)) + return; + + if (gpio_get_value(spild->gpio_ipc_srdy) == SPI_GPIOLEVEL_LOW) + return; + + if (get_console_suspended()) + return; + + if (spild->spi_state == SPI_STATE_END) + return; + + spild->spi_state = SPI_STATE_RX_WAIT; + spild->spi_timer_rx_state = SPI_STATE_TIME_START; + + gpio_set_value(spild->gpio_ipc_sub_mrdy, SPI_GPIOLEVEL_HIGH); + /* Start TX timer */ + spild->spi_rx_timer.expires = + jiffies + ((SPI_TIMER_RX_WAIT_TIME * HZ) / 1000); + add_timer(&spild->spi_rx_timer); + + /* check SUBSRDY state */ + while (gpio_get_value(spild->gpio_ipc_sub_srdy) == + SPI_GPIOLEVEL_LOW) { + if (spild->spi_timer_rx_state == SPI_STATE_TIME_OVER) { + pr_err("[SPI] ERROR(Failed MASTER RX:%d ms)", + SPI_TIMER_RX_WAIT_TIME); + + spild->spi_timer_rx_state = SPI_STATE_TIME_START; + + gpio_set_value(spild->gpio_ipc_sub_mrdy, + SPI_GPIOLEVEL_LOW); + + /* change state SPI_MAIN_STATE_RX_WAIT */ + /* to SPI_STATE_IDLE */ + spild->spi_state = SPI_STATE_IDLE; + + return; + } + } + /* Stop TX timer */ + del_timer(&spild->spi_rx_timer); + + if (spild->spi_state == SPI_STATE_START + || spild->spi_state == SPI_STATE_END + || spild->spi_state == SPI_STATE_INIT) + return; + + spi_packet_buf = spild->buff; + spi_sync_buf = spild->sync_buff; + + memset(spi_packet_buf, 0, SPI_MAX_PACKET_SIZE); + memset(spi_sync_buf, 0, SPI_MAX_PACKET_SIZE); + + if (spi_tx_rx_sync((void *)spi_sync_buf, (void *)spi_packet_buf, + SPI_MAX_PACKET_SIZE) == 0) { + /* parsing SPI packet */ + if (spi_buff_read(spild) > 0) { + /* call function for send data to IPC, RAW, RFS */ + for (i = 0; i < spild->max_ipc_dev; i++) { + iod = spild->iod[i]; + while ((skb = skb_dequeue(&spild->skb_rxq[i])) + != NULL) { + if (iod->recv(iod, ld, skb->data, + skb->len) < 0) + pr_err("[LNK/E] <%s:%s> recv fail\n", + __func__, ld->name); + dev_kfree_skb_any(skb); + } + } + } + } else { + pr_err("%s %s\n", "[SPI] ERROR : spi_rx_work :", + "spi sync failed"); + + /* add cp reset when spi sync fail */ + iod = link_get_iod_with_format(&spild->ld, IPC_FMT); + + if (iod) + iod->modem_state_changed(iod, + STATE_CRASH_RESET); + } + + spild->spi_state = SPI_STATE_RX_TERMINATE; + + gpio_set_value(spild->gpio_ipc_sub_mrdy, SPI_GPIOLEVEL_LOW); + + /* change state SPI_MAIN_STATE_RX_WAIT to SPI_STATE_IDLE */ + spild->spi_state = SPI_STATE_IDLE; + spi_start_data_send(); +} + +unsigned int sprd_crc_calc(char *buf_ptr, unsigned int len) +{ + unsigned int i; + unsigned short crc = 0; + + while (len-- != 0) { + for (i = 0x80; i != 0 ; i = i>>1) { + if ((crc & 0x8000) != 0) { + crc = crc << 1 ; + crc = crc ^ 0x1021; + } else { + crc = crc << 1 ; + } + + if ((*buf_ptr & i) != 0) + crc = crc ^ 0x1021; + } + buf_ptr++; + } + + return crc; + +} + +unsigned short sprd_crc_calc_fdl(unsigned short *src, int len) +{ + unsigned int sum = 0; + unsigned short SourceValue, DestValue = 0; + unsigned short lowSourceValue, hiSourceValue = 0; + + /* Get sum value of the source.*/ + while (len > 1) { + SourceValue = *src++; + DestValue = 0; + lowSourceValue = (SourceValue & 0xFF00) >> 8; + hiSourceValue = (SourceValue & 0x00FF) << 8; + DestValue = lowSourceValue | hiSourceValue; + + sum += DestValue; + len -= 2; + } + + if (len == 1) + sum += *((unsigned char *) src); + + sum = (sum >> 16) + (sum & 0x0FFFF); + sum += (sum >> 16); + + return ~sum; +} + +int encode_msg(struct sprd_image_buf *img, int bcrc) +{ + u16 crc; /* CRC value*/ + u8 *src_ptr; /* source buffer pointer*/ + int dest_len; /* output buffer length*/ + u8 *dest_ptr; /* dest buffer pointer*/ + u8 high_crc, low_crc = 0; + register int curr; + + /* CRC Check. */ + src_ptr = img->tx_b; + + /* CRC Check. */ + if (bcrc) + crc = sprd_crc_calc(src_ptr, img->tx_size); + else + crc = sprd_crc_calc_fdl + ((unsigned short *)src_ptr, img->tx_size); + + high_crc = (crc>>8) & 0xFF; + low_crc = crc & 0xFF; + + /* Get the total size to be allocated.*/ + dest_len = 0; + + for (curr = 0; curr < img->tx_size; curr++) { + switch (*(src_ptr+curr)) { + case HDLC_FLAG: + case HDLC_ESCAPE: + dest_len += 2; + break; + default: + dest_len++; + break; + } + } + + switch (low_crc) { + case HDLC_FLAG: + case HDLC_ESCAPE: + dest_len += 2; + break; + default: + dest_len++; + } + + switch (high_crc) { + case HDLC_FLAG: + case HDLC_ESCAPE: + dest_len += 2; + break; + default: + dest_len++; + } + + dest_ptr = kmalloc(dest_len + 2, GFP_ATOMIC); + /* Memory Allocate fail.*/ + if (dest_ptr == NULL) + return -ENOMEM; + + *dest_ptr = HDLC_FLAG; + dest_len = 1; + + /* do escape*/ + for (curr = 0; curr < img->tx_size; curr++) { + switch (*(src_ptr+curr)) { + case HDLC_FLAG: + case HDLC_ESCAPE: + *(dest_ptr + dest_len++) = HDLC_ESCAPE; + *(dest_ptr + dest_len++) = + *(src_ptr + curr) ^ HDLC_ESCAPE_MASK; + break; + default: + *(dest_ptr + dest_len++) = *(src_ptr + curr); + break; + } + } + + switch (high_crc) { + case HDLC_FLAG: + case HDLC_ESCAPE: + *(dest_ptr + dest_len++) = HDLC_ESCAPE; + *(dest_ptr + dest_len++) = high_crc ^ HDLC_ESCAPE_MASK; + break; + default: + *(dest_ptr + dest_len++) = high_crc; + } + + switch (low_crc) { + case HDLC_FLAG: + case HDLC_ESCAPE: + *(dest_ptr + dest_len++) = HDLC_ESCAPE; + *(dest_ptr + dest_len++) = low_crc ^ HDLC_ESCAPE_MASK; + break; + default: + *(dest_ptr + dest_len++) = low_crc; + } + + + *(dest_ptr + dest_len++) = HDLC_FLAG; + + memcpy(img->encoded_tx_b, dest_ptr, dest_len); + img->encoded_tx_size = dest_len; + + kfree(dest_ptr); + return 0; +} + +int decode_msg(struct sprd_image_buf *img, int bcrc) +{ + u16 crc; /* CRC value*/ + u8 *src_ptr; /* source buffer pointer*/ + int dest_len; /* output buffer length*/ + u8 *dest_ptr; /* dest buffer pointer*/ + register int curr; + + /* Check if exist End Flag.*/ + src_ptr = img->rx_b; + + dest_len = 0; + + if (img->rx_size < 4) + return -EINVAL; + + /* Get the total size to be allocated for decoded message.*/ + for (curr = 1; curr < img->rx_size - 1; curr++) { + switch (*(src_ptr + curr)) { + case HDLC_ESCAPE: + curr++; + dest_len++; + break; + default: + dest_len++; + break; + } + } + + /* Allocate meomory for decoded message*/ + dest_ptr = kmalloc(dest_len, GFP_ATOMIC); + /* Memory allocate fail.*/ + if (dest_ptr == NULL) + return -ENOMEM; + + memset(dest_ptr, 0, dest_len); + + curr = 0; + dest_len = 0; + /* Do de-escape.*/ + for (curr = 1; curr < img->rx_size - 1; curr++) { + switch (*(src_ptr + curr)) { + case HDLC_ESCAPE: + curr++; + *(dest_ptr + dest_len) = + *(src_ptr + curr) ^ HDLC_ESCAPE_MASK; + break; + default: + *(dest_ptr + dest_len) = *(src_ptr + curr); + break; + } + + dest_len = dest_len + 1; + } + + /* CRC Check. */ + if (bcrc) + crc = sprd_crc_calc(dest_ptr, dest_len); + else + crc = sprd_crc_calc_fdl((unsigned short *)dest_ptr, dest_len); + + if (crc != CRC_16_L_OK) { + pr_err("CRC error : 0x%X", crc); + kfree(dest_ptr); + return -EPERM; + } + + memcpy(img->decoded_rx_b, dest_ptr, dest_len - CRC_CHECK_SIZE); + img->decoded_rx_size = dest_len - CRC_CHECK_SIZE ; + + kfree(dest_ptr); + return 0; +} + +static int spi_send_modem_bin_execute_cmd + (u8 *spi_ptr, u32 spi_size, u16 spi_type, + u16 spi_crc, struct sprd_image_buf *sprd_img) +{ + int i, retval; + u16 send_packet_size; + u8 *send_packet_data; + u16 d1_crc; + u16 d2_crc = spi_crc; + u16 type = spi_type; + + /* D1 */ + send_packet_size = spi_size; /* u32 -> u16 */ + sprd_img->tx_size = 6; + M_16_SWAP(d2_crc); + memcpy(sprd_img->tx_b, &send_packet_size, sizeof(send_packet_size)); + memcpy((sprd_img->tx_b+2), &type, sizeof(type)); + memcpy((sprd_img->tx_b+4), &d2_crc, sizeof(d2_crc)); + + d1_crc = sprd_crc_calc_fdl + ((unsigned short *)sprd_img->tx_b, sprd_img->tx_size); + M_16_SWAP(d1_crc); + memcpy((sprd_img->tx_b+6), &d1_crc, sizeof(d1_crc)); + sprd_img->tx_size += 2; + + if (down_timeout(&p_spild->srdy_sem, 2 * HZ)) { + pr_err("(%d) SRDY TimeOUT!!! SRDY : %d, SEM : %d\n", + __LINE__, + gpio_get_value(p_spild->gpio_modem_bin_srdy), + p_spild->srdy_sem.count); + goto err; + } + + retval = spi_tx_rx_sync + (sprd_img->tx_b, sprd_img->rx_b, sprd_img->tx_size); + if (retval != 0) { + pr_err("(%d) spi sync error : %d\n", + __LINE__, retval); + goto err; + } + + if ((type == 0x0003) || (type == 0x0004)) { + pr_err("D2 Skip!!\n"); + goto ACK; + } + + /* D2 */ + send_packet_data = spi_ptr; + + if (down_timeout(&p_spild->srdy_sem, 2 * HZ)) { + pr_err("(%d) SRDY TimeOUT!!! SRDY : %d, SEM : %d\n", __LINE__, + gpio_get_value(p_spild->gpio_modem_bin_srdy), + p_spild->srdy_sem.count); + goto err; + } + + retval = spi_tx_rx_sync + (send_packet_data, sprd_img->rx_b, send_packet_size); + if (retval != 0) { + pr_err("(%d) spi sync error : %d\n", __LINE__, retval); + goto err; + } + +ACK: + + if (p_spild->is_cp_reset) { + while (!gpio_get_value(p_spild->gpio_modem_bin_srdy)) + ; + } else { + if (down_timeout(&p_spild->srdy_sem, 2 * HZ)) { + pr_err("(%d) SRDY TimeOUT!!! SRDY : %d, SEM : %d\n", + __LINE__, + gpio_get_value(p_spild->gpio_modem_bin_srdy), + p_spild->srdy_sem.count); + + pr_err("[SPI DUMP] TX_D1(%d) : [%02x %02x %02x %02x %02x %02x %02x %02x]\n", + sprd_img->tx_size, sprd_img->tx_b[0], + sprd_img->tx_b[1], sprd_img->tx_b[2], + sprd_img->tx_b[3], sprd_img->tx_b[4], + sprd_img->tx_b[5], sprd_img->tx_b[6], + sprd_img->tx_b[7]); + pr_err("[SPI DUMP] TX_D2(%d) :[%02x %02x %02x %02x %02x %02x %02x %02x]\n", + send_packet_size, spi_ptr[0], spi_ptr[1], + spi_ptr[2], spi_ptr[3], spi_ptr[4], + spi_ptr[5], spi_ptr[6], spi_ptr[7]); + + /* WA (CP Reset) jongmoon.suh */ + if (gpio_get_value(p_spild->gpio_modem_bin_srdy)) + ; + else + goto err; + + } + } + + memset(sprd_img->tx_b, 0, SPRD_BLOCK_SIZE+10); + retval = spi_tx_rx_sync(sprd_img->tx_b, sprd_img->rx_b, 8); + if (retval != 0) { + pr_err("(%d) spi sync error : %d\n", + __LINE__, retval); + goto err; + } + + memcpy(sprd_img->decoded_rx_b, sprd_img->rx_b, 4); + + if ((*(sprd_img->decoded_rx_b+0) == 0x00) && \ + (*(sprd_img->decoded_rx_b+1) == 0x80) && \ + (*(sprd_img->decoded_rx_b+2) == 0x00) && \ + (*(sprd_img->decoded_rx_b+3) == 0x00)) { + pr_debug("[SPRD] CP sent ACK"); + } else { + pr_err("Transfer ACK error! srdy_sem = %d\n", + p_spild->srdy_sem.count); + pr_err("[SPI DUMP] RX(%d) : [ ", sprd_img->rx_size); + for (i = 0; i < 15; i++) + pr_err("%02x ", *((u8 *)(sprd_img->rx_b + i))); + pr_err("]"); + + goto err; + } + + return retval; + +err: + return -EINVAL; +} + +static int spi_send_modem_bin_xmit_img + (enum image_type type, struct image_buf *img) +{ + int retval = 0; + struct sprd_image_buf sprd_img; + unsigned int data_size; + unsigned int send_size = 0; + unsigned int rest_size = 0; + unsigned int spi_size = 0; + unsigned int address = 0; + unsigned int fdl1_size = 0; + /* No Translate */ + u16 crc = 0; + u16 spi_type = 0; + + unsigned char *spi_ptr; + unsigned char *ptr; + int i, j = 0; + + u16 sprd_packet_size = SPRD_BLOCK_SIZE; + + sprd_img.tx_b = kmalloc(SPRD_BLOCK_SIZE*2, GFP_ATOMIC); + if (!sprd_img.tx_b) { + pr_err("(%d) tx_b kmalloc fail.", + __LINE__); + return -ENOMEM; + } + memset(sprd_img.tx_b, 0, SPRD_BLOCK_SIZE*2); + + sprd_img.rx_b = kmalloc(SPRD_BLOCK_SIZE*2, GFP_ATOMIC); + if (!sprd_img.rx_b) { + pr_err("(%d) rx_b kmalloc fail.", + __LINE__); + retval = -ENOMEM; + goto err3; + } + memset(sprd_img.rx_b, 0, SPRD_BLOCK_SIZE*2); + + sprd_img.encoded_tx_b = kmalloc(SPRD_BLOCK_SIZE*2, GFP_ATOMIC); + if (!sprd_img.encoded_tx_b) { + pr_err("(%d) encoded_tx_b kmalloc fail.", + __LINE__); + retval = -ENOMEM; + goto err2; + } + memset(sprd_img.encoded_tx_b, 0, SPRD_BLOCK_SIZE*2); + + sprd_img.decoded_rx_b = kmalloc(SPRD_BLOCK_SIZE*2, GFP_ATOMIC); + if (!sprd_img.decoded_rx_b) { + pr_err("(%d) encoded_rx_b kmalloc fail.", + __LINE__); + retval = -ENOMEM; + goto err1; + } + memset(sprd_img.decoded_rx_b, 0, SPRD_BLOCK_SIZE*2); + + pr_debug("(%d) spi_send_modem_bin_xmit_img type : %d.\n", + __LINE__, type); + memcpy(&fdl1_size, (void *)(p_spild->p_virtual_buff + 4), 4); + + switch (type) { + case MODEM_MAIN: + memcpy(&img->address, + (void *)(p_spild->p_virtual_buff + 8), 4); + memcpy(&img->length, + (void *)(p_spild->p_virtual_buff + 12), 4); + img->buf = (unsigned char *) + (p_spild->p_virtual_buff + 0x30 + fdl1_size); + img->offset = img->length + fdl1_size + 0x30; + pr_debug("(%d) spi_send_modem_bin_xmit_img save MAIN to img.\n", + __LINE__); + + break; + + case MODEM_DSP: + memcpy(&img->address, + (void *)(p_spild->p_virtual_buff + 16), 4); + memcpy(&img->length, + (void *)(p_spild->p_virtual_buff + 20), 4); + img->buf = (unsigned char *) + (p_spild->p_virtual_buff + img->offset); + img->offset += img->length; + pr_debug("(%d) spi_send_modem_bin_xmit_img save DSP to img.\n", + __LINE__); + + break; + + case MODEM_NV: + memcpy(&img->address, + (void *)(p_spild->p_virtual_buff + 24), 4); + memcpy(&img->length, + (void *)(p_spild->p_virtual_buff + 28), 4); + img->buf = (unsigned char *) + (p_spild->p_virtual_buff + img->offset); + img->offset += img->length; + pr_debug("(%d) spi_send_modem_bin_xmit_img save NV to img.\n", + __LINE__); + + break; + + case MODEM_EFS: + memcpy(&img->address, + (void *)(p_spild->p_virtual_buff + 32), 4); + memcpy(&img->length, + (void *)(p_spild->p_virtual_buff + 36), 4); + img->buf = (unsigned char *) + (p_spild->p_virtual_buff + img->offset); + img->offset += img->length; + pr_debug("(%d) spi_send_modem_bin_xmit_img save EFS to img.\n", + __LINE__); + + break; + case MODEM_RUN: + memset(sprd_img.encoded_tx_b, 0, SPRD_BLOCK_SIZE*2); + sprd_img.encoded_tx_size = 0; + spi_type = 0x0004; + crc = 0; + + spi_ptr = sprd_img.encoded_tx_b; + spi_size = sprd_img.encoded_tx_size; + + retval = spi_send_modem_bin_execute_cmd + (spi_ptr, spi_size, spi_type, crc, &sprd_img); + if (retval < 0) { + pr_err("(%d) spi_send_modem_bin_execute_cmd fail : %d", + __LINE__, retval); + goto err0; + } + return retval; + + default: + pr_err("(%d) spi_send_modem_bin_xmit_img wrong : %d.", + __LINE__, type); + goto err0; + } + + pr_debug("(%d) Start send img. size : %d\n", + __LINE__, img->length); + + ptr = img->buf; + data_size = sprd_packet_size; + rest_size = img->length; + address = img->address; + + M_32_SWAP(img->address); + M_32_SWAP(img->length); + + /* Send Transfer Start */ + sprd_img.tx_size = 8; + memcpy((sprd_img.tx_b+0), &img->address, sizeof(img->address)); + memcpy((sprd_img.tx_b+4), &img->length, sizeof(img->length)); + + spi_type = 0x0001; + crc = sprd_crc_calc_fdl + ((unsigned short *)sprd_img.tx_b, sprd_img.tx_size); + memcpy(sprd_img.encoded_tx_b, sprd_img.tx_b, sprd_img.tx_size); + sprd_img.encoded_tx_size = sprd_img.tx_size; + + spi_ptr = sprd_img.encoded_tx_b; + spi_size = sprd_img.encoded_tx_size; + + pr_debug("(%d) [Transfer Start, Type = %d, Packet = %d]\n", + __LINE__, type, sprd_packet_size); + retval = spi_send_modem_bin_execute_cmd + (spi_ptr, spi_size, spi_type, crc, &sprd_img); + if (retval < 0) { + pr_err("(%d) spi_send_modem_bin_execute_cmd fail : %d", + __LINE__, retval); + goto err0; + } + M_32_SWAP(img->length); + + /* Send Data */ + for (i = 0; send_size < img->length; i++) { + if (rest_size < sprd_packet_size) + data_size = rest_size; + + sprd_img.encoded_tx_size = sprd_packet_size; + for (j = 0; j < data_size; j++) + *(sprd_img.encoded_tx_b+j) = *(ptr + j); + + spi_type = 0x0002; + crc = sprd_crc_calc_fdl + ((unsigned short *)sprd_img.encoded_tx_b, + sprd_img.encoded_tx_size); + + spi_ptr = sprd_img.encoded_tx_b; + spi_size = sprd_img.encoded_tx_size; + + retval = spi_send_modem_bin_execute_cmd + (spi_ptr, spi_size, spi_type, crc, &sprd_img); + if (retval < 0) { + pr_err("(%d) spi_send_modem_bin_execute_cmd fail : %d, %d", + __LINE__, retval, i); + goto err0; + } + + send_size += data_size; + rest_size -= data_size; + ptr += data_size; + + if (!(i % 100)) + pr_debug("(%d) [%d] 0x%x size done, rest size: 0x%x\n", + __LINE__, i, send_size, rest_size); + } + + /* Send Transfer End */ + memset(sprd_img.encoded_tx_b, 0, SPRD_BLOCK_SIZE * 2); + sprd_img.encoded_tx_size = 0; + + spi_type = 0x0003; + crc = 0; + + spi_ptr = sprd_img.encoded_tx_b; + spi_size = sprd_img.encoded_tx_size; + + pr_debug("(%d) [Transfer END]\n", __LINE__); + retval = spi_send_modem_bin_execute_cmd + (spi_ptr, spi_size, spi_type, crc, &sprd_img); + if (retval < 0) { + pr_err("(%d) spi_send_modem_bin_execute_cmd fail : %d", + __LINE__, retval); + goto err0; + } + +err0: + kfree(sprd_img.decoded_rx_b); +err1: + kfree(sprd_img.encoded_tx_b); +err2: + kfree(sprd_img.rx_b); +err3: + kfree(sprd_img.tx_b); + + return retval; + +} + +static void spi_send_modem_bin(struct work_struct *send_modem_w) +{ + int retval; + struct image_buf img; + unsigned long tick1, tick2 = 0; + + tick1 = jiffies_to_msecs(jiffies); + + retval = spi_send_modem_bin_xmit_img(MODEM_MAIN, &img); + if (retval < 0) { + pr_err("(%d) spi_send_modem_bin_xmit_img fail : %d", + __LINE__, retval); + goto err; + } + + retval = spi_send_modem_bin_xmit_img(MODEM_DSP, &img); + if (retval < 0) { + pr_err("(%d) spi_send_modem_bin_xmit_img fail : %d", + __LINE__, retval); + goto err; + } + + retval = spi_send_modem_bin_xmit_img(MODEM_NV, &img); + if (retval < 0) { + pr_err("(%d) spi_send_modem_bin_xmit_img fail : %d", + __LINE__, retval); + goto err; + } + + retval = spi_send_modem_bin_xmit_img(MODEM_EFS, &img); + if (retval < 0) { + pr_err("(%d) spi_send_modem_bin_xmit_img fail : %d", + __LINE__, retval); + goto err; + } + + retval = spi_send_modem_bin_xmit_img(MODEM_RUN, &img); + if (retval < 0) { + pr_err("(%d) spi_send_modem_bin_xmit_img fail : %d", + __LINE__, retval); + goto err; + } + + p_spild->send_modem_spi = 0; + p_spild->is_cp_reset = 0; + tick2 = jiffies_to_msecs(jiffies); + pr_info("Downloading takes %lu msec\n", (tick2-tick1)); + + complete_all(&p_spild->ril_init); + sprd_boot_done = 1; + p_spild->ril_send_cnt = 0; + +err: + return; + +} + +static inline int _request_mem(struct ipc_spi *od) +{ + if (!p_spild->p_virtual_buff) { + pr_err("what\n"); + od->mmio = vmalloc(od->size); + if (!od->mmio) { + pr_err("(%d) Failed to vmalloc size : %lu\n", __LINE__, + od->size); + + return -EBUSY; + } else { + pr_err("(%d) vmalloc Done. mmio : 0x%08x\n", __LINE__, + (u32)od->mmio); + } + } + + memset((void *)od->mmio, 0, od->size); + + p_spild->p_virtual_buff = od->mmio; + + return 0; +} + +void spi_tx_timer_callback(unsigned long param) +{ + if (p_spild->spi_state == SPI_STATE_TX_WAIT) { + p_spild->spi_timer_tx_state = SPI_STATE_TIME_OVER; + pr_err("[SPI] spi_tx_timer_callback -timer expires\n"); + } +} + +void spi_rx_timer_callback(unsigned long param) +{ + if (p_spild->spi_state == SPI_STATE_RX_WAIT) { + p_spild->spi_timer_rx_state = SPI_STATE_TIME_OVER; + pr_err("[SPI] spi_rx_timer_callback -timer expires\n"); + } +} + +int spi_sema_init(void) +{ + pr_info("[SPI] Srdy sema init\n"); + sema_init(&p_spild->srdy_sem, 0); + p_spild->send_modem_spi = 1; + return 0; +} + +static void spi_work(struct work_struct *work) +{ + int signal_code; + + struct spi_work_type *spi_wq = + container_of(work, struct spi_work_type, work); + + signal_code = spi_wq->signal_code; + + if (p_spild->spi_state == SPI_STATE_END + || p_spild->spi_state == SPI_STATE_START) { + kfree(spi_wq); + return; + } + + switch (signal_code) { + case SPI_WORK_SEND: + if (p_spild->spi_state == SPI_STATE_IDLE) + spi_tx_work(); + break; + case SPI_WORK_RECEIVE: + if (p_spild->spi_state == SPI_STATE_IDLE + || p_spild->spi_state == SPI_STATE_TX_TERMINATE + || p_spild->spi_state == SPI_STATE_RX_TERMINATE) + spi_rx_work(); + break; + + default: + pr_err("[SPI] ERROR(No signal_code in spi_work[%d])\n", + signal_code); + break; + } + + kfree(spi_wq); + if (wake_lock_active(&p_spild->spi_wake_lock)) { + wake_unlock(&p_spild->spi_wake_lock); + pr_err("[SPI] [%s](%d) spi_wakelock unlocked .\n", + __func__, __LINE__); + } +} + +static int spi_init_ipc(struct spi_link_device *spild) +{ + struct link_device *ld = &spild->ld; + + int i; + + /* Make aliases to each IO device */ + for (i = 0; i < MAX_DEV_FORMAT; i++) + spild->iod[i] = link_get_iod_with_format(ld, i); + + spild->iod[IPC_RAW] = spild->iod[IPC_MULTI_RAW]; + + /* List up the IO devices connected to each IPC channel */ + for (i = 0; i < MAX_DEV_FORMAT; i++) { + if (spild->iod[i]) + pr_err("[LNK] <%s:%s> spild->iod[%d]->name = %s\n", + __func__, ld->name, i, spild->iod[i]->name); + else + pr_err("[LNK] <%s:%s> No spild->iod[%d]\n", + __func__, ld->name, i); + } + + ld->mode = LINK_MODE_IPC; + + return 0; +} + +static int spi_thread(void *data) +{ + struct spi_link_device *spild = (struct spi_link_device *)data; + + if (lpcharge == 1) { + pr_err("[LPM MODE] spi_thread_exit!\n"); + return 0; + } + + daemonize("spi_thread"); + + pr_info("[%s] spi_thread start.\n", __func__); + p_spild->boot_done = 1; + + wait_for_completion(&p_spild->ril_init); + + pr_info("[%s] ril_init completed.\n", __func__); + + pr_info("<%s> wait 2 sec... srdy : %d\n", + __func__, gpio_get_value(spild->gpio_ipc_srdy)); + msleep_interruptible(1700); + + while (gpio_get_value(spild->gpio_ipc_srdy)) + ; + pr_info("(%s) cp booting... Done.\n", __func__); + + spi_init_ipc(spild); + + pr_info("[spi_thread] Start IPC Communication. SRDY : %d\n", + gpio_get_value(spild->gpio_ipc_srdy)); + + /* CP booting is already completed, just set submrdy to high */ + if (gpio_get_value(spild->gpio_ipc_sub_srdy) == SPI_GPIOLEVEL_HIGH) { + gpio_set_value(spild->gpio_ipc_sub_mrdy, SPI_GPIOLEVEL_HIGH); + pr_err("[spi_thread] CP booting is already completed\n"); + } + /* CP booting is not completed. + set submrdy to high and wait until subsrdy is high */ + else { + pr_err("[spi_thread] CP booting is not completed. wait...\n"); + + gpio_set_value(spild->gpio_ipc_sub_mrdy, SPI_GPIOLEVEL_HIGH); + do { + msleep_interruptible(5); + } while (gpio_get_value(spild->gpio_ipc_sub_srdy) == + SPI_GPIOLEVEL_LOW); + + pr_err("[spi_thread] CP booting is done...\n"); + } + + if (p_spild->spi_is_restart) + msleep_interruptible(100); + else + msleep_interruptible(30); + + gpio_set_value(spild->gpio_ipc_sub_mrdy, SPI_GPIOLEVEL_LOW); + + pr_info("(%s) spi sync done.\n", __func__); + + spild->spi_state = SPI_STATE_IDLE; + p_spild->spi_is_restart = 0; + + return 0; +} + +static int spi_probe(struct spi_device *spi) +{ + int ret; + + p_spi = spi; + p_spi->mode = SPI_MODE_1; + p_spi->bits_per_word = 32; + + ret = spi_setup(p_spi); + if (ret != 0) { + + pr_err("[%s] spi setup error\n", __func__); + + return ret; + } + + pr_info("[%s] spi probe done\n", __func__); + + return ret; +} + +static int spi_remove(struct spi_device *spi) +{ + return 0; +} + +static struct spi_driver spi_driver = { + .probe = spi_probe, + .remove = __devexit_p(spi_remove), + .driver = { + .name = "modem_if_spi", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, +}; + +static int spi_link_init(void) +{ + int ret; + struct ipc_spi *od; + struct task_struct *th; + struct link_device *ld = &p_spild->ld; + + p_spild->gpio_modem_bin_srdy = p_spild->gpio_ipc_srdy; + + pr_info("(%d) gpio_mrdy : %d, gpio_srdy : %d(%d)\n", __LINE__, + p_spild->gpio_ipc_mrdy, p_spild->gpio_modem_bin_srdy, + gpio_get_value(p_spild->gpio_ipc_srdy)); + + od = kzalloc(sizeof(struct ipc_spi), GFP_KERNEL); + if (!od) { + pr_err("(%d) failed to allocate device\n", __LINE__); + ret = -ENOMEM; + goto err; + } + + od->base = 0; + od->size = SZ_16M; /* 16M */ + if (p_spild->p_virtual_buff) + od->mmio = p_spild->p_virtual_buff; + ret = _request_mem(od); + if (ret) + goto err; + + init_completion(&p_spild->ril_init); + sema_init(&p_spild->srdy_sem, 0); + + INIT_WORK(&p_spild->send_modem_w, + spi_send_modem_bin); + + wake_lock_init(&p_spild->spi_wake_lock, + WAKE_LOCK_SUSPEND, + "samsung-spiwakelock"); + + /* Register SPI Srdy interrupt handler */ + ret = spi_register_isr(gpio_to_irq(p_spild->gpio_ipc_srdy), + spi_srdy_irq_handler, IRQF_TRIGGER_RISING, + "spi_srdy_rising", ld); + + if (ret) + goto err; + + /* Register SPI SubSrdy interrupt handler */ + ret = spi_register_isr(gpio_to_irq(p_spild->gpio_ipc_sub_srdy), + spi_subsrdy_irq_handler, IRQF_TRIGGER_RISING, + "spi_subsrdy_rising", ld); + + if (ret) + goto err; + + th = kthread_create(spi_thread, (void *)p_spild, "spi_thread"); + if (IS_ERR(th)) { + pr_err("kernel_thread() failed\n"); + goto err; + } + wake_up_process(th); + + pr_info("[%s] Done\n", __func__); + return 0; + +err: + kfree(od); + return ret; +} + +/*===================================== +* Description spi restart for CP slient reset +=====================================*/ +void spi_set_restart(void) +{ + struct link_device *ld = &p_spild->ld; + + pr_info("[SPI] spi_set_restart(spi_main_state=[%d])\n", + (int)p_spild->spi_state); + + gpio_set_value(p_spild->gpio_ipc_sub_mrdy, SPI_GPIOLEVEL_LOW); + + p_spild->spi_state = SPI_STATE_END; + p_spild->spi_is_restart = 1; + + /* Flush SPI work queue */ + flush_workqueue(p_spild->spi_wq); + + /* Unregister SPI Srdy interrupt handler */ + spi_unregister_isr(gpio_to_irq(p_spild->gpio_ipc_srdy), ld); + + /* Unregister SPI SubSrdy interrupt handler */ + spi_unregister_isr(gpio_to_irq(p_spild->gpio_ipc_sub_srdy), ld); + + if (wake_lock_active(&p_spild->spi_wake_lock)) { + wake_unlock(&p_spild->spi_wake_lock); + pr_err("[SPI] [%s](%d) spi_wakelock unlocked.\n", + __func__, __LINE__); + } + wake_lock_destroy(&p_spild->spi_wake_lock); +} + + +int spi_thread_restart(void) +{ + int retval; + + p_spild->send_modem_spi = 1; + p_spild->is_cp_reset = 1; + sprd_boot_done = 0; + + pr_info("[modem_if_spi] spi_thread_restart\n"); + + spi_set_restart(); + + /* Create SPI device */ + retval = spi_link_init(); + if (retval) + goto exit; + + return 0; + +exit: + return retval; +} +EXPORT_SYMBOL(spi_thread_restart); + +struct link_device *spi_create_link_device(struct platform_device *pdev) +{ + struct spi_link_device *spild = NULL; + struct link_device *ld; + struct modem_data *pdata; + + int ret; + int i; + + /* Get the platform data */ + pdata = (struct modem_data *)pdev->dev.platform_data; + if (!pdata) { + pr_err("[LNK/E] <%s> pdata == NULL\n", __func__); + goto err; + } + + pr_info("[LNK] <%s> link device = %s\n", __func__, pdata->link_name); + pr_info("[LNK] <%s> modem = %s\n", __func__, pdata->name); + + /* Alloc SPI link device structure */ + p_spild = spild = kzalloc(sizeof(struct spi_link_device), GFP_KERNEL); + if (!spild) { + pr_err("[LNK/E] <%s> Failed to kzalloc()\n", __func__); + goto err; + } + ld = &spild->ld; + + /* Extract modem data and SPI control data from the platform data */ + ld->mdm_data = pdata; + ld->name = "spi"; + + if (ld->aligned) + pr_err("[LNK] <%s> Aligned access is required!!!\n", __func__); + + + ld->send = spi_send; + + INIT_LIST_HEAD(&ld->list); + + skb_queue_head_init(&ld->sk_fmt_tx_q); + skb_queue_head_init(&ld->sk_raw_tx_q); + skb_queue_head_init(&ld->sk_rfs_tx_q); + ld->skb_txq[IPC_FMT] = &ld->sk_fmt_tx_q; + ld->skb_txq[IPC_RAW] = &ld->sk_raw_tx_q; + ld->skb_txq[IPC_RFS] = &ld->sk_rfs_tx_q; + + spild->spi_wq = create_singlethread_workqueue("spi_wq"); + if (!spild->spi_wq) { + pr_err("[LNK/E] <%s> Fail to create workqueue for spi_wq\n", + __func__); + goto err; + } + + spild->spi_state = SPI_STATE_END; + spild->max_ipc_dev = IPC_RFS+1; /* FMT, RAW, RFS */ + + for (i = 0; i < spild->max_ipc_dev; i++) + skb_queue_head_init(&spild->skb_rxq[i]); + + /* Prepare a clean buffer for SPI access */ + spild->buff = kzalloc(SPI_MAX_PACKET_SIZE, GFP_KERNEL); + spild->sync_buff = kzalloc(SPI_MAX_PACKET_SIZE, GFP_KERNEL); + + memset(spild->buff , 0, SPI_MAX_PACKET_SIZE); + memset(spild->sync_buff , 0, SPI_MAX_PACKET_SIZE); + + if (!spild->buff) { + pr_err("[LNK/E] <%s> Failed to alloc spild->buff\n", __func__); + goto err; + } + + /* Initialize SPI pin value */ + spild->gpio_ipc_mrdy = pdata->gpio_ipc_mrdy; + spild->gpio_ipc_srdy = pdata->gpio_ipc_srdy; + spild->gpio_ipc_sub_mrdy = pdata->gpio_ipc_sub_mrdy; + spild->gpio_ipc_sub_srdy = pdata->gpio_ipc_sub_srdy; + + spild->gpio_ap_cp_int1 = pdata->gpio_ap_cp_int1; + spild->gpio_ap_cp_int2 = pdata->gpio_ap_cp_int2; + + /* Create SPI Timer */ + init_timer(&spild->spi_tx_timer); + + spild->spi_tx_timer.expires = + jiffies + ((SPI_TIMER_TX_WAIT_TIME * HZ) / 1000); + + spild->spi_tx_timer.data = 0; + spild->spi_tx_timer.function = spi_tx_timer_callback; + + init_timer(&spild->spi_rx_timer); + + spild->spi_rx_timer.expires = + jiffies + ((SPI_TIMER_RX_WAIT_TIME * HZ) / 1000); + + spild->spi_rx_timer.data = 0; + spild->spi_rx_timer.function = spi_rx_timer_callback; + + ret = spi_register_driver(&spi_driver); + if (ret < 0) { + pr_err("spi_register_driver() fail : %d\n", ret); + goto err; + } + + /* Create SPI device */ + ret = spi_link_init(); + if (ret) + goto err; + + /* creat work queue thread */ + p_spild->ipc_spi_wq = create_singlethread_workqueue("ipc_spi_wq"); + + if (!p_spild->ipc_spi_wq) { + pr_err("[%s] get workqueue thread fail\n", + __func__); + + ret = -ENOMEM; + goto err; + } + + return ld; + +err: + kfree(spild->buff); + kfree(spild); + return NULL; +} diff --git a/drivers/misc/modem_if/modem_link_device_spi.h b/drivers/misc/modem_if/modem_link_device_spi.h new file mode 100644 index 0000000..2289a46 --- /dev/null +++ b/drivers/misc/modem_if/modem_link_device_spi.h @@ -0,0 +1,257 @@ +/* + * Copyright (C) 2011 Google, Inc. + * Copyright (C) 2010 Samsung Electronics. + * + * 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. + * + */ +#ifndef __MODEM_LINK_DEVICE_SPI_H__ +#define __MODEM_LINK_DEVICE_SPI_H__ + +#include <linux/wakelock.h> +#include <linux/workqueue.h> +#include <linux/timer.h> +#include <linux/platform_data/modem.h> + + +#define SPI_TIMER_TX_WAIT_TIME 60 /* ms */ +#define SPI_TIMER_RX_WAIT_TIME 500 /* ms */ + +#define SPI_MAX_PACKET_SIZE (2048 * 6) +#define SPI_TASK_EVENT_COUNT 64 +#define SPI_DATA_BOF 0x7F +#define SPI_DATA_EOF 0x7E +#define SPI_DATA_FF_PADDING_HEADER 0xFFFFFFFF + +#define SPI_DATA_MUX_NORMAL_MASK 0x0F +#define SPI_DATA_MUX_MORE_H 0x10 +#define SPI_DATA_MUX_MORE_M 0x20 +#define SPI_DATA_MUX_MORE_T 0x30 + +#define SPI_DATA_MUX_SIZE 2 +#define SPI_DATA_LENGTH_SIZE 4 +#define SPI_DATA_BOF_SIZE 1 +#define SPI_DATA_INNER_LENGTH_SIZE 4 +#define SPI_DATA_IPC_INNER_LENGTH_SIZE 2 +#define SPI_DATA_IPC_INNER_CONTROL_SIZE 1 +#define SPI_DATA_EOF_SIZE 1 +#define SPI_DATA_HEADER_SIZE (SPI_DATA_MUX_SIZE+ \ + SPI_DATA_LENGTH_SIZE+SPI_DATA_BOF_SIZE+SPI_DATA_EOF_SIZE) +#define SPI_DATA_HEADER_SIZE_FRONT (SPI_DATA_MUX_SIZE+ \ + SPI_DATA_LENGTH_SIZE+SPI_DATA_BOF_SIZE) +#define SPI_DATA_IPC_INNER_HEADER_SIZE \ + (SPI_DATA_IPC_INNER_LENGTH_SIZE+ \ + SPI_DATA_IPC_INNER_CONTROL_SIZE) +#define SPI_DATA_SIZE(X) (SPI_DATA_HEADER_SIZE+X) + +#define SPI_DATA_LENGTH_OFFSET SPI_DATA_MUX_SIZE +#define SPI_DATA_BOF_OFFSET (SPI_DATA_LENGTH_OFFSET+ \ + SPI_DATA_LENGTH_SIZE) +#define SPI_DATA_DATA_OFFSET (SPI_DATA_BOF_OFFSET+ \ + SPI_DATA_BOF_SIZE) +#define SPI_DATA_EOF_OFFSET(X) (SPI_DATA_DATA_OFFSET+X) + +#define SPI_DATA_PACKET_HEADER_SIZE 4 +#define SPI_DATA_PACKET_MUX_ERROR_SPARE_SIZE 4 +#define SPI_DATA_PACKET_MAX_PACKET_BODY_SIZE \ + (SPI_MAX_PACKET_SIZE-SPI_DATA_PACKET_HEADER_SIZE- \ + SPI_DATA_PACKET_MUX_ERROR_SPARE_SIZE) + +#define SPI_DATA_MIN_SIZE (SPI_DATA_HEADER_SIZE*2) +#define SPI_DATA_MAX_SIZE_PER_PACKET (SPI_MAX_PACKET_SIZE- \ + SPI_DATA_PACKET_HEADER_SIZE-SPI_DATA_HEADER_SIZE- \ + SPI_DATA_PACKET_MUX_ERROR_SPARE_SIZE) + +#define SPI_DATA_DIVIDE_BUFFER_SIZE SPI_MAX_PACKET_SIZE + +struct spi_work_type { + struct work_struct work; + int signal_code; +}; + +enum spi_msg_t { + SPI_WORK_SEND, + SPI_WORK_RECEIVE +}; + +enum spi_state_t { + SPI_STATE_START, /* before init complete */ + SPI_STATE_INIT, /* initialising */ + SPI_STATE_IDLE, /* suspend. Waiting for event */ + /* state to start tx. Become from idle */ + SPI_STATE_TX_START, + SPI_STATE_TX_CONFIRM, + /* (in case of master) */ + /* wait srdy rising interrupt to check slave preparing */ + /* (in case of slave) */ + /* wait submrdy rising interrupt to check sync complete */ + SPI_STATE_TX_WAIT, + SPI_STATE_TX_SENDING, /* tx data sending */ + SPI_STATE_TX_TERMINATE, + SPI_STATE_TX_MORE, + + /* in case of slave, wait submrdy rising interrupt to */ + SPI_STATE_RX_WAIT, + + /* check sync complete then it starts to read buffer */ + SPI_STATE_RX_MORE, + SPI_STATE_RX_TERMINATE, + SPI_STATE_END /* spi task is stopped */ +}; + +enum spi_timer_state_t { + SPI_STATE_TIME_START, + SPI_STATE_TIME_OVER +}; + +enum spi_gpiolevel_t { + SPI_GPIOLEVEL_LOW = 0, + SPI_GPIOLEVEL_HIGH +}; + +enum spi_data_type_t { + SPI_DATA_MUX_IPC = 0x01, + SPI_DATA_MUX_RAW = 0x02, + SPI_DATA_MUX_RFS = 0x03, + SPI_DATA_MUX_CMD = 0x04, +}; + +struct spi_data_packet_header { + /* 12bit : packet size less than SPI_DEV_PACKET_SIZE */ + unsigned long current_data_size:31; + /* 1bit : packet division flag */ + unsigned long more:1; +}; + +struct spi_link_device { + struct link_device ld; + + /* Link to SPI control functions dependent on each platform */ + int max_ipc_dev; + + /* Wakelock for SPI device */ + struct wake_lock spi_wake_lock; + /* Workqueue for modem bin transfers */ + struct workqueue_struct *ipc_spi_wq; + + /* SPI state */ + int spi_state; + int spi_is_restart; + + /* SPI Timer state */ + int spi_timer_tx_state; + int spi_timer_rx_state; + + /* SPI GPIO pins */ + unsigned int gpio_ipc_mrdy; + unsigned int gpio_ipc_srdy; + unsigned int gpio_ipc_sub_mrdy; + unsigned int gpio_ipc_sub_srdy; + + unsigned int gpio_modem_bin_srdy; + + unsigned int gpio_ap_cp_int1; + unsigned int gpio_ap_cp_int2; + + /* value for checking spi pin status */ + unsigned long mrdy_low_time_save; + unsigned long mrdy_high_time_save; + + /* SPI Wait Timer */ + struct timer_list spi_tx_timer; + struct timer_list spi_rx_timer; + + struct workqueue_struct *spi_wq; + struct spi_work_type spi_work; + + /* For send modem bin */ + struct work_struct send_modem_w; + + struct io_device *iod[MAX_DEV_FORMAT]; + struct sk_buff_head skb_rxq[MAX_DEV_FORMAT]; + + /* Multi-purpose miscellaneous buffer */ + u8 *buff; + u8 *sync_buff; + + struct completion ril_init; + struct semaphore srdy_sem; + + int send_modem_spi; + int is_cp_reset; + int boot_done; + int ril_send_modem_img; + unsigned long ril_send_cnt; + + void __iomem *p_virtual_buff; +}; + +/* converts from struct link_device* to struct xxx_link_device* */ +#define to_spi_link_device(linkdev) \ + container_of(linkdev, struct spi_link_device, ld) + +extern unsigned int lpcharge; +extern int get_console_suspended(void); +static void spi_work(struct work_struct *work); + +/* Send SPRD main image through SPI */ +#define SPRD_BLOCK_SIZE 32768 + +enum image_type { + MODEM_MAIN, + MODEM_DSP, + MODEM_NV, + MODEM_EFS, + MODEM_RUN, +}; + +struct image_buf { + unsigned int length; + unsigned int offset; + unsigned int address; + unsigned char *buf; +}; + +struct sprd_image_buf { + u8 *tx_b; + u8 *rx_b; + u8 *encoded_tx_b; + u8 *decoded_rx_b; + + int tx_size; + int rx_size; + int encoded_tx_size; + int decoded_rx_size; +}; + +/* CRC */ +#define CRC_16_L_OK 0x0 +#define HDLC_FLAG 0x7E +#define HDLC_ESCAPE 0x7D +#define HDLC_ESCAPE_MASK 0x20 +#define CRC_CHECK_SIZE 0x02 + +#define M_32_SWAP(a) { \ + u32 _tmp; \ + _tmp = a; \ + ((u8 *)&a)[0] = ((u8 *)&_tmp)[3]; \ + ((u8 *)&a)[1] = ((u8 *)&_tmp)[2]; \ + ((u8 *)&a)[2] = ((u8 *)&_tmp)[1]; \ + ((u8 *)&a)[3] = ((u8 *)&_tmp)[0]; \ + } + +#define M_16_SWAP(a) { \ + u16 _tmp; \ + _tmp = (u16)a; \ + ((u8 *)&a)[0] = ((u8 *)&_tmp)[1]; \ + ((u8 *)&a)[1] = ((u8 *)&_tmp)[0]; \ + } + +#endif diff --git a/drivers/misc/modem_if/modem_link_device_usb.c b/drivers/misc/modem_if/modem_link_device_usb.c index 615971e..5b7c98b 100644 --- a/drivers/misc/modem_if/modem_link_device_usb.c +++ b/drivers/misc/modem_if/modem_link_device_usb.c @@ -32,6 +32,8 @@ #include "modem_utils.h" #include "modem_link_pm_usb.h" +#include <mach/regs-gpio.h> + #define URB_COUNT 4 static int usb_tx_urb_with_skb(struct usb_link_device *usb_ld, @@ -61,8 +63,8 @@ static int start_ipc(struct link_device *ld, struct io_device *iod) struct usb_link_device *usb_ld = to_usb_link_device(ld); struct if_usb_devdata *pipe_data = &usb_ld->devdata[IF_USB_FMT_EP]; - if (usb_ld->link_pm_data->hub_handshake_done) { - mif_err("Aleady send start ipc, skip start ipc\n"); + if (has_hub(usb_ld) && usb_ld->link_pm_data->hub_handshake_done) { + mif_err("Already send start ipc, skip start ipc\n"); err = 0; goto exit; } @@ -73,8 +75,9 @@ static int start_ipc(struct link_device *ld, struct io_device *iod) goto exit; } - if (usb_ld->if_usb_initstates == INIT_IPC_START_DONE) { - mif_debug("aleady IPC started\n"); + if (has_hub(usb_ld) && + usb_ld->if_usb_initstates == INIT_IPC_START_DONE) { + mif_debug("Already IPC started\n"); err = 0; goto exit; } @@ -262,8 +265,8 @@ static void usb_tx_complete(struct urb *urb) usb_mark_last_busy(urb->dev); ret = pm_runtime_put_autosuspend(&urb->dev->dev); - if (ret < 0) - mif_debug("pm_runtime_put_autosuspend failed : ret(%d)\n", ret); + if (ret < 0 && ret != -EAGAIN) + mif_debug("pm_runtime_put_autosuspend failed: %d\n", ret); usb_free_urb(urb); dev_kfree_skb_any(skb); } @@ -274,9 +277,18 @@ static void if_usb_force_disconnect(struct work_struct *work) container_of(work, struct usb_link_device, disconnect_work); struct usb_device *udev = usb_ld->usbdev; + /* if already disconnected before run this workqueue */ + if (!udev || !(&udev->dev) || !usb_ld->if_usb_connected) + return; + + /* disconnect udev's parent if usb hub used */ + if (has_hub(usb_ld)) + udev = udev->parent; + pm_runtime_get_sync(&udev->dev); if (udev->state != USB_STATE_NOTATTACHED) { - mif_info("force disconnect by modem not responding!!\n"); + usb_force_disconnect(udev); + mif_info("force disconnect\n"); } pm_runtime_put_autosuspend(&udev->dev); } @@ -438,6 +450,7 @@ static void usb_tx_work(struct work_struct *work) static int if_usb_suspend(struct usb_interface *intf, pm_message_t message) { struct usb_link_device *usb_ld = usb_get_intfdata(intf); + struct link_pm_data *pm_data = usb_ld->link_pm_data; int i; if (atomic_inc_return(&usb_ld->suspend_count) == IF_USB_DEVNUM_MAX) { @@ -446,8 +459,8 @@ static int if_usb_suspend(struct usb_interface *intf, pm_message_t message) for (i = 0; i < IF_USB_DEVNUM_MAX; i++) usb_kill_anchored_urbs(&usb_ld->devdata[i].reading); - if (usb_ld->link_pm_data->cpufreq_unlock) - usb_ld->link_pm_data->cpufreq_unlock(); + if (pm_data->freq_unlock) + pm_data->freq_unlock(&usb_ld->usbdev->dev); wake_unlock(&usb_ld->susplock); } @@ -472,10 +485,16 @@ static void post_resume_work(struct work_struct *work) { struct usb_link_device *usb_ld = container_of(work, struct usb_link_device, post_resume_work.work); + struct link_pm_data *pm_data = usb_ld->link_pm_data; + struct usb_device *udev = usb_ld->usbdev; - /* lock cpu frequency when L2->L0 */ - if (usb_ld->link_pm_data->cpufreq_lock) - usb_ld->link_pm_data->cpufreq_lock(); + /* if already disconnected before run this workqueue */ + if (!udev || !(&udev->dev) || !usb_ld->if_usb_connected) + return; + + /* lock cpu/bus frequency when L2->L0 */ + if (pm_data->freq_lock) + pm_data->freq_lock(&udev->dev); } static void wait_enumeration_work(struct work_struct *work) @@ -532,10 +551,11 @@ static int if_usb_resume(struct usb_interface *intf) skb = urb->context; dev_kfree_skb_any(skb); usb_free_urb(urb); - if (pm_runtime_put_autosuspend( - &usb_ld->usbdev->dev) < 0) - mif_debug( - "pm_runtime_put_autosuspend fail\n"); + ret = pm_runtime_put_autosuspend( + &usb_ld->usbdev->dev); + if (ret < 0 && ret != -EAGAIN) + mif_debug("pm_runtime_put_autosuspend " + "failed: %d\n", ret); } } SET_SLAVE_WAKEUP(usb_ld->pdata, 1); @@ -576,9 +596,11 @@ static void if_usb_disconnect(struct usb_interface *intf) { struct usb_link_device *usb_ld = usb_get_intfdata(intf); struct usb_device *usbdev = usb_ld->usbdev; + struct link_pm_data *pm_data = usb_ld->link_pm_data; int dev_id = intf->altsetting->desc.bInterfaceNumber; struct if_usb_devdata *pipe_data = &usb_ld->devdata[dev_id]; + usb_set_intfdata(intf, NULL); pipe_data->disconnected = 1; @@ -602,6 +624,7 @@ static void if_usb_disconnect(struct usb_interface *intf) cancel_delayed_work_sync(&usb_ld->ld.tx_delayed_work); usb_put_dev(usbdev); usb_ld->usbdev = NULL; + pm_runtime_forbid(pm_data->root_hub); } } @@ -642,7 +665,6 @@ static int __devinit if_usb_probe(struct usb_interface *intf, dev_id, id, usb_ld); usb_ld->usbdev = usbdev; - usb_get_dev(usbdev); for (i = 0; i < IF_USB_DEVNUM_MAX; i++) { @@ -726,7 +748,8 @@ static int __devinit if_usb_probe(struct usb_interface *intf, usb_ld->host_wake_timeout_flag = 0; if (gpio_get_value(usb_ld->pdata->gpio_phone_active)) { - int delay = usb_ld->link_pm_data->autosuspend_delay_ms ?: + struct link_pm_data *pm_data = usb_ld->link_pm_data; + int delay = pm_data->autosuspend_delay_ms ?: DEFAULT_AUTOSUSPEND_DELAY_MS; pm_runtime_set_autosuspend_delay(&usbdev->dev, delay); dev = &usbdev->dev; @@ -741,13 +764,13 @@ static int __devinit if_usb_probe(struct usb_interface *intf, dev_name(ehci_dev)); pm_runtime_allow(ehci_dev); - if (has_hub(usb_ld)) { - usb_ld->link_pm_data->hub_status = - (usb_ld->link_pm_data->root_hub) ? - HUB_STATE_PREACTIVE : HUB_STATE_ACTIVE; - } + if (!pm_data->autosuspend) + pm_runtime_forbid(dev); - usb_ld->link_pm_data->root_hub = root_hub; + if (has_hub(usb_ld)) + link_pm_preactive(pm_data); + + pm_data->root_hub = root_hub; } usb_ld->flow_suspend = 0; @@ -764,6 +787,14 @@ static int __devinit if_usb_probe(struct usb_interface *intf, usb_change_modem_state(usb_ld, STATE_LOADER_DONE); } + /* check dynamic switching gpio received + * before usb enumeration is completed + */ + if (ld->mc->need_switch_to_usb) { + ld->mc->need_switch_to_usb = false; + rawdevs_set_tx_link(ld->msd, LINKDEV_USB); + } + return 0; out2: @@ -791,6 +822,13 @@ irqreturn_t usb_resume_irq(int irq, void *data) wake_status = hwup; irq_set_irq_type(irq, hwup ? IRQF_TRIGGER_LOW : IRQF_TRIGGER_HIGH); + /* + * exynos BSP has problem when using level interrupt. + * If we change irq type from interrupt handler, + * we can get level interrupt twice. + * this is temporary solution until SYS.LSI resolve this problem. + */ + __raw_writel(eint_irq_to_bit(irq), S5P_EINT_PEND(EINT_REG_NR(irq))); wake_lock_timeout(&usb_ld->gpiolock, 100); mif_err("< H-WUP %d\n", hwup); @@ -945,6 +983,15 @@ static void __exit if_usb_exit(void) usb_deregister(&if_usb_driver); } +bool usb_is_enumerated(struct modem_shared *msd) +{ + struct link_device *ld = find_linkdev(msd, LINKDEV_USB); + if (ld) + return to_usb_link_device(ld)->usbdev != NULL; + else + return false; +} + /* lte specific functions */ diff --git a/drivers/misc/modem_if/modem_link_device_usb.h b/drivers/misc/modem_if/modem_link_device_usb.h index 44f6b1b..8233fd1 100644 --- a/drivers/misc/modem_if/modem_link_device_usb.h +++ b/drivers/misc/modem_if/modem_link_device_usb.h @@ -52,7 +52,6 @@ enum IPC_INIT_STATUS { enum hub_status { HUB_STATE_OFF, /* usb3503 0ff*/ HUB_STATE_RESUMMING, /* usb3503 on, but enummerattion was not yet*/ - HUB_STATE_PREACTIVE, HUB_STATE_ACTIVE, /* hub and CMC221 enumerate */ }; @@ -128,5 +127,6 @@ do { \ #define has_hub(usb_ld) ((usb_ld)->link_pm_data->has_usbhub) irqreturn_t usb_resume_irq(int irq, void *data); +bool usb_is_enumerated(struct modem_shared *msd); #endif diff --git a/drivers/misc/modem_if/modem_link_pm_usb.c b/drivers/misc/modem_if/modem_link_pm_usb.c index 244256f..1b2614e 100644 --- a/drivers/misc/modem_if/modem_link_pm_usb.c +++ b/drivers/misc/modem_if/modem_link_pm_usb.c @@ -27,15 +27,34 @@ #include "modem_link_pm_usb.h" +static inline void start_hub_work(struct link_pm_data *pm_data, int delay) +{ + if (pm_data->hub_work_running == false) { + pm_data->hub_work_running = true; + wake_lock(&pm_data->hub_lock); + mif_debug("link_pm_hub_work is started\n"); + } + + schedule_delayed_work(&pm_data->link_pm_hub, msecs_to_jiffies(delay)); +} + +static inline void end_hub_work(struct link_pm_data *pm_data) +{ + wake_unlock(&pm_data->hub_lock); + pm_data->hub_work_running = false; + mif_debug("link_pm_hub_work is done\n"); +} + bool link_pm_is_connected(struct usb_link_device *usb_ld) { if (has_hub(usb_ld)) { - if (usb_ld->link_pm_data->hub_init_lock) + struct link_pm_data *pm_data = usb_ld->link_pm_data; + if (pm_data->hub_init_lock) return false; - if (usb_ld->link_pm_data->hub_status != HUB_STATE_ACTIVE) { - schedule_delayed_work( - &usb_ld->link_pm_data->link_pm_hub, 0); + if (pm_data->hub_status == HUB_STATE_OFF) { + if (pm_data->hub_work_running == false) + start_hub_work(pm_data, 0); return false; } } @@ -48,27 +67,40 @@ bool link_pm_is_connected(struct usb_link_device *usb_ld) return true; } +void link_pm_preactive(struct link_pm_data *pm_data) +{ + if (pm_data->root_hub) { + mif_info("pre-active\n"); + pm_data->hub_on_retry_cnt = 0; + complete(&pm_data->hub_active); + pm_runtime_put_sync(pm_data->root_hub); + } + + pm_data->hub_status = HUB_STATE_ACTIVE; +} + static void link_pm_hub_work(struct work_struct *work) { int err; struct link_pm_data *pm_data = container_of(work, struct link_pm_data, link_pm_hub.work); - if (pm_data->hub_status == HUB_STATE_ACTIVE) + if (pm_data->hub_status == HUB_STATE_ACTIVE) { + end_hub_work(pm_data); return; + } if (!pm_data->port_enable) { mif_err("mif: hub power func not assinged\n"); + end_hub_work(pm_data); return; } - wake_lock(&pm_data->hub_lock); /* If kernel if suspend, wait the ehci resume */ if (pm_data->dpm_suspending) { mif_info("dpm_suspending\n"); - schedule_delayed_work(&pm_data->link_pm_hub, - msecs_to_jiffies(500)); - goto exit; + start_hub_work(pm_data, 500); + return; } switch (pm_data->hub_status) { @@ -88,11 +120,11 @@ static void link_pm_hub_work(struct work_struct *work) pm_data->hub_status = HUB_STATE_OFF; if (pm_data->root_hub) pm_runtime_put_sync(pm_data->root_hub); - goto exit; + end_hub_work(pm_data); + } else { + /* resume root hub */ + start_hub_work(pm_data, 100); } - /* resume root hub */ - schedule_delayed_work(&pm_data->link_pm_hub, - msecs_to_jiffies(100)); break; case HUB_STATE_RESUMMING: if (pm_data->hub_on_retry_cnt++ > 50) { @@ -100,34 +132,26 @@ static void link_pm_hub_work(struct work_struct *work) pm_data->hub_status = HUB_STATE_OFF; if (pm_data->root_hub) pm_runtime_put_sync(pm_data->root_hub); + end_hub_work(pm_data); + } else { + mif_info("hub resumming: %d\n", + pm_data->hub_on_retry_cnt); + start_hub_work(pm_data, 200); } - mif_trace("hub resumming\n"); - schedule_delayed_work(&pm_data->link_pm_hub, - msecs_to_jiffies(200)); - break; - case HUB_STATE_PREACTIVE: - pm_data->hub_status = HUB_STATE_ACTIVE; - mif_trace("hub active\n"); - pm_data->hub_on_retry_cnt = 0; - wake_unlock(&pm_data->hub_lock); - complete(&pm_data->hub_active); - if (pm_data->root_hub) - pm_runtime_put_sync(pm_data->root_hub); break; } exit: return; } -static int link_pm_hub_standby(struct link_pm_data *pm_data) +static int link_pm_hub_standby(void *args) { + struct link_pm_data *pm_data = args; struct usb_link_device *usb_ld = pm_data->usb_ld; int err = 0; - mif_info("wait hub standby\n"); - if (!pm_data->port_enable) { - mif_err("hub power func not assinged\n"); + mif_err("port power func not assinged\n"); return -ENODEV; } @@ -136,6 +160,13 @@ static int link_pm_hub_standby(struct link_pm_data *pm_data) mif_err("hub off fail err=%d\n", err); pm_data->hub_status = HUB_STATE_OFF; + + /* this function is atomic. + * make force disconnect in workqueue.. + */ + if (pm_data->usb_ld->if_usb_connected) + schedule_work(&usb_ld->disconnect_work); + return err; } @@ -169,6 +200,7 @@ static long link_pm_ioctl(struct file *file, unsigned int cmd, { int value, err = 0; struct link_pm_data *pm_data = file->private_data; + struct usb_link_device *usb_ld = pm_data->usb_ld; mif_info("cmd: 0x%08x\n", cmd); @@ -182,15 +214,13 @@ static long link_pm_ioctl(struct file *file, unsigned int cmd, case IOCTL_LINK_GET_HOSTWAKE: return !gpio_get_value(pm_data->gpio_link_hostwake); case IOCTL_LINK_CONNECTED: - return pm_data->usb_ld->if_usb_connected; - case IOCTL_LINK_PORT_ON: /* hub only */ + return usb_ld->if_usb_connected; + case IOCTL_LINK_PORT_ON: /* ignore cp host wakeup irq, set the hub_init_lock when AP try CP off and release hub_init_lock when CP boot done */ pm_data->hub_init_lock = 0; - if (pm_data->root_hub) { - pm_runtime_resume(pm_data->root_hub); - pm_runtime_forbid(pm_data->root_hub->parent); - } + if (pm_data->root_hub) + pm_runtime_get_sync(pm_data->root_hub); if (pm_data->port_enable) { err = pm_data->port_enable(2, 1); if (err < 0) { @@ -200,17 +230,7 @@ static long link_pm_ioctl(struct file *file, unsigned int cmd, pm_data->hub_status = HUB_STATE_RESUMMING; } break; - case IOCTL_LINK_PORT_OFF: /* hub only */ - if (pm_data->usb_ld->if_usb_connected) { - struct usb_device *udev = - pm_data->usb_ld->usbdev->parent; - pm_runtime_get_sync(&udev->dev); - if (udev->state != USB_STATE_NOTATTACHED) { - usb_force_disconnect(udev); - pr_info("force disconnect maybe cp-reset!!\n"); - } - pm_runtime_put_autosuspend(&udev->dev); - } + case IOCTL_LINK_PORT_OFF: err = link_pm_hub_standby(pm_data); if (err < 0) { mif_err("usb3503 active fail\n"); @@ -218,7 +238,6 @@ static long link_pm_ioctl(struct file *file, unsigned int cmd, } pm_data->hub_init_lock = 1; pm_data->hub_handshake_done = 0; - break; default: break; @@ -227,6 +246,50 @@ exit: return err; } +static ssize_t show_autosuspend(struct device *dev, + struct device_attribute *attr, char *buf) +{ + char *p = buf; + struct miscdevice *miscdev = dev_get_drvdata(dev); + struct link_pm_data *pm_data = container_of(miscdev, + struct link_pm_data, miscdev); + struct usb_link_device *usb_ld = pm_data->usb_ld; + + p += sprintf(buf, "%s\n", pm_data->autosuspend ? "on" : "off"); + + return p - buf; +} + +static ssize_t store_autosuspend(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct miscdevice *miscdev = dev_get_drvdata(dev); + struct link_pm_data *pm_data = container_of(miscdev, + struct link_pm_data, miscdev); + struct usb_link_device *usb_ld = pm_data->usb_ld; + struct task_struct *task = get_current(); + char taskname[TASK_COMM_LEN]; + + mif_info("autosuspend: %s: %s(%d)'\n", + buf, get_task_comm(taskname, task), task->pid); + + if (!strncmp(buf, "on", 2)) { + pm_data->autosuspend = true; + if (usb_ld->usbdev) + pm_runtime_allow(&usb_ld->usbdev->dev); + } else if (!strncmp(buf, "off", 3)) { + pm_data->autosuspend = false; + if (usb_ld->usbdev) + pm_runtime_forbid(&usb_ld->usbdev->dev); + } + + return count; +} + +static struct device_attribute attr_autosuspend = + __ATTR(autosuspend, S_IRUGO | S_IWUSR, + show_autosuspend, store_autosuspend); + static int link_pm_open(struct inode *inode, struct file *file) { struct link_pm_data *pm_data = @@ -253,11 +316,13 @@ static int link_pm_notifier_event(struct notifier_block *this, { struct link_pm_data *pm_data = container_of(this, struct link_pm_data, pm_notifier); + struct usb_link_device *usb_ld = pm_data->usb_ld; switch (event) { case PM_SUSPEND_PREPARE: pm_data->dpm_suspending = true; - link_pm_hub_standby(pm_data); + if (has_hub(usb_ld)) + link_pm_hub_standby(pm_data); return NOTIFY_OK; case PM_POST_SUSPEND: pm_data->dpm_suspending = false; @@ -286,12 +351,12 @@ int link_pm_init(struct usb_link_device *usb_ld, void *data) pm_data->gpio_link_slavewake = pm_pdata->gpio_link_slavewake; pm_data->link_reconnect = pm_pdata->link_reconnect; pm_data->port_enable = pm_pdata->port_enable; - pm_data->cpufreq_lock = pm_pdata->cpufreq_lock; - pm_data->cpufreq_unlock = pm_pdata->cpufreq_unlock; + pm_data->freq_lock = pm_pdata->freq_lock; + pm_data->freq_unlock = pm_pdata->freq_unlock; pm_data->autosuspend_delay_ms = pm_pdata->autosuspend_delay_ms; + pm_data->autosuspend = true; pm_data->usb_ld = usb_ld; - pm_data->link_pm_active = false; usb_ld->link_pm_data = pm_data; pm_data->miscdev.minor = MISC_DYNAMIC_MINOR; @@ -304,6 +369,13 @@ int link_pm_init(struct usb_link_device *usb_ld, void *data) goto err_misc_register; } + err = device_create_file(pm_data->miscdev.this_device, + &attr_autosuspend); + if (err) { + mif_err("fail to create file: autosuspend: %d\n", err); + goto err_create_file; + } + pm_data->hub_init_lock = 1; irq = gpio_to_irq(usb_ld->pdata->gpio_host_wakeup); err = request_threaded_irq(irq, NULL, usb_resume_irq, @@ -319,12 +391,16 @@ int link_pm_init(struct usb_link_device *usb_ld, void *data) if (has_hub(usb_ld)) { init_completion(&pm_data->hub_active); pm_data->hub_status = HUB_STATE_OFF; - pm_pdata->p_hub_status = &pm_data->hub_status; pm_data->hub_handshake_done = 0; pm_data->root_hub = NULL; + + pm_pdata->hub_standby = link_pm_hub_standby; + pm_pdata->hub_pm_data = pm_data; + wake_lock_init(&pm_data->hub_lock, WAKE_LOCK_SUSPEND, "modem_hub_enum_lock"); INIT_DELAYED_WORK(&pm_data->link_pm_hub, link_pm_hub_work); + pm_data->hub_work_running = false; } pm_data->pm_notifier.notifier_call = link_pm_notifier_event; @@ -333,10 +409,9 @@ int link_pm_init(struct usb_link_device *usb_ld, void *data) return 0; err_request_irq: +err_create_file: misc_deregister(&pm_data->miscdev); err_misc_register: kfree(pm_data); return err; } - - diff --git a/drivers/misc/modem_if/modem_link_pm_usb.h b/drivers/misc/modem_if/modem_link_pm_usb.h index 103e4f4..d26af76 100644 --- a/drivers/misc/modem_if/modem_link_pm_usb.h +++ b/drivers/misc/modem_if/modem_link_pm_usb.h @@ -41,30 +41,25 @@ struct link_pm_data { int hub_handshake_done; struct wake_lock hub_lock; struct delayed_work link_pm_hub; + bool hub_work_running; int hub_on_retry_cnt; struct device *root_hub; - struct delayed_work link_pm_work; - struct delayed_work link_pm_start; - struct delayed_work link_reconnect_work; - bool resume_requested; - bool link_pm_active; - - struct wake_lock l2_wake; - struct wake_lock boot_wake; struct notifier_block pm_notifier; bool dpm_suspending; int (*port_enable)(int, int); - int (*cpufreq_lock)(void); - int (*cpufreq_unlock)(void); + int (*freq_lock)(struct device *dev); + int (*freq_unlock)(struct device *dev); int autosuspend_delay_ms; /* if zero, the default value is used */ + bool autosuspend; }; bool link_pm_set_active(struct usb_link_device *usb_ld); bool link_pm_is_connected(struct usb_link_device *usb_ld); +void link_pm_preactive(struct link_pm_data *pm_data); int link_pm_init(struct usb_link_device *usb_ld, void *data); #endif diff --git a/drivers/misc/modem_if/modem_modemctl_device_cbp72.c b/drivers/misc/modem_if/modem_modemctl_device_cbp72.c index b8d2711..2617be8 100644 --- a/drivers/misc/modem_if/modem_modemctl_device_cbp72.c +++ b/drivers/misc/modem_if/modem_modemctl_device_cbp72.c @@ -43,8 +43,10 @@ static irqreturn_t phone_active_handler(int irq, void *arg) phone_state, phone_reset, phone_active); if (phone_reset && phone_active) { - phone_state = STATE_ONLINE; - mc->bootd->modem_state_changed(mc->bootd, phone_state); + if (mc->phone_state == STATE_BOOTING) { + phone_state = STATE_ONLINE; + mc->bootd->modem_state_changed(mc->bootd, phone_state); + } } else if (phone_reset && !phone_active) { if (mc->phone_state == STATE_ONLINE) { phone_state = STATE_CRASH_EXIT; @@ -70,6 +72,10 @@ static int cbp72_on(struct modem_ctl *mc) { mif_info("start!!!\n"); + /* prevent sleep during bootloader downloading */ + if (!wake_lock_active(&mc->mc_wake_lock)) + wake_lock(&mc->mc_wake_lock); + gpio_set_value(mc->gpio_cp_on, 0); if (mc->gpio_cp_off) gpio_set_value(mc->gpio_cp_off, 1); @@ -178,6 +184,9 @@ static int cbp72_boot_off(struct modem_ctl *mc) return -ENXIO; } mc->bootd->modem_state_changed(mc->bootd, STATE_ONLINE); + + wake_unlock(&mc->mc_wake_lock); + return 0; } @@ -253,6 +262,8 @@ int cbp72_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata) return ret; } + wake_lock_init(&mc->mc_wake_lock, WAKE_LOCK_SUSPEND, "cbp72_wake_lock"); + ret = enable_irq_wake(irq); if (ret) mif_err("enable_irq_wake fail (%d)\n", ret); diff --git a/drivers/misc/modem_if/modem_modemctl_device_cmc221.c b/drivers/misc/modem_if/modem_modemctl_device_cmc221.c index 2d564e9..fe8ce69 100644 --- a/drivers/misc/modem_if/modem_modemctl_device_cmc221.c +++ b/drivers/misc/modem_if/modem_modemctl_device_cmc221.c @@ -30,9 +30,9 @@ #define PIF_TIMEOUT (180 * HZ) #define DPRAM_INIT_TIMEOUT (30 * HZ) - static void mc_state_fsm(struct modem_ctl *mc) { + struct link_device *ld = get_current_link(mc->iod); int cp_on = gpio_get_value(mc->gpio_cp_on); int cp_reset = gpio_get_value(mc->gpio_cp_reset); int cp_active = gpio_get_value(mc->gpio_phone_active); @@ -46,6 +46,7 @@ static void mc_state_fsm(struct modem_ctl *mc) if (!cp_on) { gpio_set_value(mc->gpio_cp_reset, 0); new_state = STATE_OFFLINE; + ld->mode = LINK_MODE_OFFLINE; mif_err("%s: new_state = PHONE_PWR_OFF\n", mc->name); } else if (old_state == STATE_ONLINE) { new_state = STATE_CRASH_EXIT; @@ -55,7 +56,6 @@ static void mc_state_fsm(struct modem_ctl *mc) } } -exit: if (old_state != new_state) { mc->bootd->modem_state_changed(mc->bootd, new_state); mc->iod->modem_state_changed(mc->iod, new_state); @@ -73,37 +73,53 @@ static irqreturn_t phone_active_handler(int irq, void *arg) return IRQ_HANDLED; } +/* TX dynamic switching between DPRAM and USB in one modem */ static irqreturn_t dynamic_switching_handler(int irq, void *arg) { struct modem_ctl *mc = (struct modem_ctl *)arg; int txpath = gpio_get_value(mc->gpio_dynamic_switching); + bool enumerated = usb_is_enumerated(mc->msd); + + mif_err("txpath=%d, enumeration=%d\n", txpath, enumerated); + + /* do not switch to USB, when USB is not enumerated. */ + if (!enumerated && txpath) { + mc->need_switch_to_usb = true; + return IRQ_HANDLED; + } - rawdevs_set_tx_link(&mc->commons, txpath ? LINKDEV_USB : LINKDEV_DPRAM); + mc->need_switch_to_usb = false; + rawdevs_set_tx_link(mc->msd, txpath ? LINKDEV_USB : LINKDEV_DPRAM); return IRQ_HANDLED; } static int cmc221_on(struct modem_ctl *mc) { - struct link_device *ld = get_current_link(mc->bootd); + struct link_device *ld = get_current_link(mc->iod); + + if (!wake_lock_active(&mc->mc_wake_lock)) + wake_lock(&mc->mc_wake_lock); + set_sromc_access(true); + + mc->phone_state = STATE_OFFLINE; + ld->mode = LINK_MODE_OFFLINE; - mif_err("%s\n", mc->bootd->name); + mif_err("%s\n", mc->name); disable_irq_nosync(mc->irq_phone_active); - gpio_set_value(mc->gpio_cp_reset, 0); gpio_set_value(mc->gpio_cp_on, 0); + msleep(100); + gpio_set_value(mc->gpio_cp_reset, 0); msleep(500); gpio_set_value(mc->gpio_cp_on, 1); - msleep(100); gpio_set_value(mc->gpio_cp_reset, 1); - mc->phone_state = STATE_OFFLINE; - return 0; } @@ -111,13 +127,14 @@ static int cmc221_off(struct modem_ctl *mc) { int cp_on = gpio_get_value(mc->gpio_cp_on); + mif_err("%s\n", mc->name); + if (mc->phone_state == STATE_OFFLINE || cp_on == 0) return 0; - mif_err("%s\n", mc->bootd->name); - - if (mc->log_fp) - mif_close_log_file(mc); + if (!wake_lock_active(&mc->mc_wake_lock)) + wake_lock(&mc->mc_wake_lock); + set_sromc_access(true); gpio_set_value(mc->gpio_cp_on, 0); @@ -128,7 +145,7 @@ static int cmc221_force_crash_exit(struct modem_ctl *mc) { struct link_device *ld = get_current_link(mc->bootd); - mif_err("%s\n", mc->bootd->name); + mif_err("%s\n", mc->name); /* Make DUMP start */ ld->force_dump(ld, mc->bootd); @@ -138,10 +155,11 @@ static int cmc221_force_crash_exit(struct modem_ctl *mc) static int cmc221_dump_reset(struct modem_ctl *mc) { - mif_err("%s\n", mc->bootd->name); + mif_err("%s\n", mc->name); - if (mc->log_fp) - mif_close_log_file(mc); + if (!wake_lock_active(&mc->mc_wake_lock)) + wake_lock(&mc->mc_wake_lock); + set_sromc_access(true); gpio_set_value(mc->gpio_host_active, 0); gpio_set_value(mc->gpio_cp_reset, 0); @@ -157,7 +175,7 @@ static int cmc221_dump_reset(struct modem_ctl *mc) static int cmc221_reset(struct modem_ctl *mc) { - mif_err("%s\n", mc->bootd->name); + mif_err("%s\n", mc->name); if (cmc221_off(mc)) return -ENXIO; @@ -172,13 +190,13 @@ static int cmc221_reset(struct modem_ctl *mc) static int cmc221_boot_on(struct modem_ctl *mc) { - mif_err("%s\n", mc->bootd->name); + mif_err("%s\n", mc->name); + + gpio_set_value(mc->gpio_pda_active, 1); mc->bootd->modem_state_changed(mc->bootd, STATE_BOOTING); mc->iod->modem_state_changed(mc->iod, STATE_BOOTING); - mif_set_log_level(mc); - return 0; } @@ -188,7 +206,7 @@ static int cmc221_boot_off(struct modem_ctl *mc) struct link_device *ld = get_current_link(mc->bootd); struct dpram_link_device *dpld = to_dpram_link_device(ld); - mif_err("%s\n", mc->bootd->name); + mif_err("%s\n", mc->name); ret = wait_for_completion_interruptible_timeout(&dpld->dpram_init_cmd, DPRAM_INIT_TIMEOUT); @@ -198,14 +216,18 @@ static int cmc221_boot_off(struct modem_ctl *mc) return -ENXIO; } - if (!mc->fs_ready) - mc->fs_ready = true; + enable_irq(mc->irq_phone_active); - if (mc->use_mif_log && mc->log_level && !mc->fs_failed && - mc->fs_ready && !mc->log_fp) - mif_open_log_file(mc); + return 0; +} - enable_irq(mc->irq_phone_active); +static int cmc221_boot_done(struct modem_ctl *mc) +{ + mif_err("%s\n", mc->name); + + set_sromc_access(false); + if (wake_lock_active(&mc->mc_wake_lock)) + wake_unlock(&mc->mc_wake_lock); return 0; } @@ -217,6 +239,7 @@ static void cmc221_get_ops(struct modem_ctl *mc) mc->ops.modem_reset = cmc221_reset; mc->ops.modem_boot_on = cmc221_boot_on; mc->ops.modem_boot_off = cmc221_boot_off; + mc->ops.modem_boot_done = cmc221_boot_done; mc->ops.modem_force_crash_exit = cmc221_force_crash_exit; mc->ops.modem_dump_reset = cmc221_dump_reset; } @@ -231,8 +254,8 @@ int cmc221_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata) mc->gpio_cp_on = pdata->gpio_cp_on; mc->gpio_cp_reset = pdata->gpio_cp_reset; mc->gpio_phone_active = pdata->gpio_phone_active; -#if 0 /*TODO: check the GPIO map*/ mc->gpio_pda_active = pdata->gpio_pda_active; +#if 0 /*TODO: check the GPIO map*/ mc->gpio_cp_dump_int = pdata->gpio_cp_dump_int; mc->gpio_flm_uart_sel = pdata->gpio_flm_uart_sel; mc->gpio_slave_wakeup = pdata->gpio_slave_wakeup; @@ -240,6 +263,7 @@ int cmc221_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata) mc->gpio_host_wakeup = pdata->gpio_host_wakeup; #endif mc->gpio_dynamic_switching = pdata->gpio_dynamic_switching; + mc->need_switch_to_usb = false; if (!mc->gpio_cp_on || !mc->gpio_cp_reset || !mc->gpio_phone_active) { mif_err("%s: ERR! no GPIO data\n", mc->name); @@ -260,6 +284,8 @@ int cmc221_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata) } mif_err("%s: PHONE_ACTIVE IRQ# = %d\n", mc->name, mc->irq_phone_active); + wake_lock_init(&mc->mc_wake_lock, WAKE_LOCK_SUSPEND, "cmc_wake_lock"); + flag = IRQF_TRIGGER_FALLING | IRQF_NO_SUSPEND; irq = mc->irq_phone_active; ret = request_irq(irq, phone_active_handler, flag, "cmc_active", mc); diff --git a/drivers/misc/modem_if/modem_modemctl_device_esc6270.c b/drivers/misc/modem_if/modem_modemctl_device_esc6270.c new file mode 100644 index 0000000..df0ff80 --- /dev/null +++ b/drivers/misc/modem_if/modem_modemctl_device_esc6270.c @@ -0,0 +1,339 @@ +/* /linux/drivers/misc/modem_if/modem_modemctl_device_esc6270.c + * + * Copyright (C) 2010 Google, Inc. + * Copyright (C) 2010 Samsung Electronics. + * + * 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/irq.h> +#include <linux/interrupt.h> +#include <linux/gpio.h> +#include <linux/delay.h> +#include <linux/wait.h> +#include <linux/sched.h> +#include <linux/platform_device.h> + +#include <linux/platform_data/modem.h> +#include "modem_prj.h" +#include <linux/regulator/consumer.h> + +#include <plat/gpio-cfg.h> + +#if defined(CONFIG_LINK_DEVICE_DPRAM) +#include "modem_link_device_dpram.h" +#elif defined(CONFIG_LINK_DEVICE_PLD) +#include "modem_link_device_pld.h" +#endif + +#if defined(CONFIG_LINK_DEVICE_DPRAM) || defined(CONFIG_LINK_DEVICE_PLD) +#include <linux/mfd/max77693.h> + +#define PIF_TIMEOUT (180 * HZ) +#define DPRAM_INIT_TIMEOUT (30 * HZ) + +static int esc6270_on(struct modem_ctl *mc) +{ + int ret; + struct link_device *ld = get_current_link(mc->iod); + + pr_info("[MODEM_IF:ESC] <%s> start!!!\n", __func__); + + if (!mc->gpio_cp_reset) { + pr_err("[MODEM_IF:ESC] no gpio data\n"); + return -ENXIO; + } + + if (mc->gpio_reset_req_n) + gpio_set_value(mc->gpio_reset_req_n, 1); + + gpio_set_value(mc->gpio_cp_reset, 1); + msleep(30); + + gpio_set_value(mc->gpio_cp_on, 1); + msleep(500); + + gpio_set_value(mc->gpio_cp_on, 0); + msleep(500); + + gpio_set_value(mc->gpio_pda_active, 1); + + mc->iod->modem_state_changed(mc->iod, STATE_BOOTING); + ld->mode = LINK_MODE_BOOT; + + return 0; +} + +static int esc6270_off(struct modem_ctl *mc) +{ + pr_info("[MODEM_IF:ESC] esc6270_off()\n"); + +#if 1 + if (!mc->gpio_cp_reset) { + pr_err("[MODEM_IF:ESC] no gpio data\n"); + return -ENXIO; + } + + gpio_set_value(mc->gpio_cp_reset, 0); + gpio_set_value(mc->gpio_cp_on, 0); +#endif + + mc->iod->modem_state_changed(mc->iod, STATE_OFFLINE); + + return 0; +} + +static int esc6270_reset(struct modem_ctl *mc) +{ + int ret = 0; + + pr_debug("[MODEM_IF:ESC] esc6270_reset()\n"); + + ret = esc6270_off(mc); + if (ret) + return -ENXIO; + + msleep(100); + + ret = esc6270_on(mc); + if (ret) + return -ENXIO; + + return 0; +} + +int esc6270_boot_on(struct modem_ctl *mc) +{ + struct link_device *ld = get_current_link(mc->iod); + + pr_info("[MODEM_IF:ESC] <%s>\n", __func__); + + /* Need to init uart byt gpio_flm_uart_sel GPIO */ + if (!mc->gpio_cp_reset || !mc->gpio_flm_uart_sel) { + pr_err("[MODEM_IF:ESC] no gpio data\n"); + return -ENXIO; + } + gpio_set_value(mc->gpio_flm_uart_sel, 1); + + pr_info(" - ESC_PHONE_ON : %d, ESC_RESET_N : %d\n", + gpio_get_value(mc->gpio_cp_on), + gpio_get_value(mc->gpio_cp_reset)); + + gpio_set_value(mc->gpio_cp_on, 0); + gpio_direction_output(mc->gpio_cp_reset, 0); + msleep(100); + + gpio_direction_output(mc->gpio_cp_on, 1); + msleep(44); + + pr_info(" - ESC_PHONE_ON : %d, ESC_RESET_N : %d\n", + gpio_get_value(mc->gpio_cp_on), + gpio_get_value(mc->gpio_cp_reset)); + + gpio_direction_input(mc->gpio_cp_reset); + msleep(600); + gpio_direction_output(mc->gpio_cp_on, 0); + + msleep(20); + pr_info(" - ESC_PHONE_ON : %d, ESC_RESET_N : %d\n", + gpio_get_value(mc->gpio_cp_on), + gpio_get_value(mc->gpio_cp_reset)); + +#if defined(CONFIG_LINK_DEVICE_PLD) + gpio_direction_output(mc->gpio_fpga_cs_n, 1); +#endif + + mc->iod->modem_state_changed(mc->iod, STATE_BOOTING); + ld->mode = LINK_MODE_BOOT; + + return 0; +} + +static int esc6270_boot_off(struct modem_ctl *mc) +{ + pr_info("[MODEM_IF:ESC] <%s>\n", __func__); + + if (!mc->gpio_flm_uart_sel) { + pr_err("[MODEM_IF:ESC] no gpio data\n"); + return -ENXIO; + } + + gpio_set_value(mc->gpio_flm_uart_sel, 0); + + mc->iod->modem_state_changed(mc->iod, STATE_OFFLINE); + + return 0; +} + +static int esc6270_active_count; + +static irqreturn_t phone_active_irq_handler(int irq, void *arg) +{ + struct modem_ctl *mc = (struct modem_ctl *)arg; + int phone_reset = 0; + int phone_active = 0; + int phone_state = 0; + int cp_dump_int = 0; + + if (!mc->gpio_cp_reset || + !mc->gpio_phone_active) { /* || !mc->gpio_cp_dump_int) { */ + pr_err("[MODEM_IF:ESC] no gpio data\n"); + return IRQ_HANDLED; + } + + phone_reset = gpio_get_value(mc->gpio_cp_reset); + phone_active = gpio_get_value(mc->gpio_phone_active); + cp_dump_int = gpio_get_value(mc->gpio_cp_dump_int); + + pr_info("[MODEM_IF:ESC] <%s> phone_reset=%d, phone_active=%d, cp_dump_int=%d\n", + __func__, phone_reset, phone_active, cp_dump_int); + + if (phone_reset && phone_active) { + phone_state = STATE_ONLINE; + if (mc->iod && mc->iod->modem_state_changed) + mc->iod->modem_state_changed(mc->iod, phone_state); + } else if (phone_reset && !phone_active) { + if (mc->phone_state == STATE_ONLINE) { + if (cp_dump_int) + phone_state = STATE_CRASH_EXIT; + else + phone_state = STATE_CRASH_RESET; + if (mc->iod && mc->iod->modem_state_changed) + mc->iod->modem_state_changed(mc->iod, + phone_state); + } + } else { + phone_state = STATE_OFFLINE; + if (mc->iod && mc->iod->modem_state_changed) + mc->iod->modem_state_changed(mc->iod, phone_state); + } + + if (phone_active) + irq_set_irq_type(mc->irq_phone_active, IRQ_TYPE_LEVEL_LOW); + else + irq_set_irq_type(mc->irq_phone_active, IRQ_TYPE_LEVEL_HIGH); + + pr_info("[MODEM_IF::ESC] <%s> phone_state = %d\n", + __func__, phone_state); + + return IRQ_HANDLED; +} + +#if defined(CONFIG_SIM_DETECT) +static irqreturn_t sim_detect_irq_handler(int irq, void *_mc) +{ + struct modem_ctl *mc = (struct modem_ctl *)_mc; + + pr_info("[MODEM_IF:ESC] <%s> gpio_sim_detect = %d\n", + __func__, gpio_get_value(mc->gpio_sim_detect)); + + if (mc->iod && mc->iod->sim_state_changed) + mc->iod->sim_state_changed(mc->iod, + !gpio_get_value(mc->gpio_sim_detect)); + + return IRQ_HANDLED; +} +#endif + +static void esc6270_get_ops(struct modem_ctl *mc) +{ + mc->ops.modem_on = esc6270_on; + mc->ops.modem_off = esc6270_off; + mc->ops.modem_reset = esc6270_reset; + mc->ops.modem_boot_on = esc6270_boot_on; + mc->ops.modem_boot_off = esc6270_boot_off; +} + +int esc6270_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata) +{ + int ret = 0; + struct platform_device *pdev; + + mc->gpio_cp_on = pdata->gpio_cp_on; + mc->gpio_reset_req_n = pdata->gpio_reset_req_n; + mc->gpio_cp_reset = pdata->gpio_cp_reset; + mc->gpio_pda_active = pdata->gpio_pda_active; + mc->gpio_phone_active = pdata->gpio_phone_active; + mc->gpio_cp_dump_int = pdata->gpio_cp_dump_int; + mc->gpio_flm_uart_sel = pdata->gpio_flm_uart_sel; + mc->gpio_cp_warm_reset = pdata->gpio_cp_warm_reset; + mc->gpio_sim_detect = pdata->gpio_sim_detect; + +#if defined(CONFIG_LINK_DEVICE_PLD) + mc->gpio_fpga_cs_n = pdata->gpio_fpga1_cs_n; +#endif + + gpio_set_value(mc->gpio_cp_reset, 0); + gpio_set_value(mc->gpio_cp_on, 0); + + pdev = to_platform_device(mc->dev); + mc->irq_phone_active = platform_get_irq_byname(pdev, "cp_active_irq"); + pr_info("[MODEM_IF:ESC] <%s> PHONE_ACTIVE IRQ# = %d\n", + __func__, mc->irq_phone_active); + + esc6270_get_ops(mc); + + if (mc->irq_phone_active) { + ret = request_irq(mc->irq_phone_active, + phone_active_irq_handler, + IRQF_TRIGGER_HIGH, + "esc_active", + mc); + if (ret) { + pr_err("[MODEM_IF:ESC] <%s> failed to request_irq IRQ# %d (err=%d)\n", + __func__, mc->irq_phone_active, ret); + return ret; + } + + ret = enable_irq_wake(mc->irq_phone_active); + if (ret) { + pr_err("[MODEM_IF:ESC] %s: failed to enable_irq_wake IRQ# %d (err=%d)\n", + __func__, mc->irq_phone_active, ret); + free_irq(mc->irq_phone_active, mc); + return ret; + } + } + +#if defined(CONFIG_SIM_DETECT) + mc->irq_sim_detect = platform_get_irq_byname(pdev, "sim_irq"); + pr_info("[MODEM_IF:ESC] <%s> SIM_DECTCT IRQ# = %d\n", + __func__, mc->irq_sim_detect); + + if (mc->irq_sim_detect) { + ret = request_irq(mc->irq_sim_detect, sim_detect_irq_handler, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + "esc_sim_detect", mc); + if (ret) { + mif_err("failed to request_irq: %d\n", ret); + mc->sim_state.online = false; + mc->sim_state.changed = false; + return ret; + } + + ret = enable_irq_wake(mc->irq_sim_detect); + if (ret) { + mif_err("failed to enable_irq_wake: %d\n", ret); + free_irq(mc->irq_sim_detect, mc); + mc->sim_state.online = false; + mc->sim_state.changed = false; + return ret; + } + + /* initialize sim_state => insert: gpio=0, remove: gpio=1 */ + mc->sim_state.online = !gpio_get_value(mc->gpio_sim_detect); + } +#endif + + return ret; +} +#endif diff --git a/drivers/misc/modem_if/modem_modemctl_device_mdm6600.c b/drivers/misc/modem_if/modem_modemctl_device_mdm6600.c index 6f1807e..2706e5e 100644 --- a/drivers/misc/modem_if/modem_modemctl_device_mdm6600.c +++ b/drivers/misc/modem_if/modem_modemctl_device_mdm6600.c @@ -258,14 +258,72 @@ int mdm6600_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata) } #endif /* CONFIG_MACH_U1_KOR_LGT */ -#if defined(CONFIG_MACH_C1CTC) || defined(CONFIG_MACH_M0_CTC) +#if defined(CONFIG_MACH_M0_CTC) || defined(CONFIG_MACH_T0_CHN_CTC) + +#if defined(CONFIG_LINK_DEVICE_DPRAM) #include "modem_link_device_dpram.h" +#elif defined(CONFIG_LINK_DEVICE_PLD) +#include "modem_link_device_pld.h" +#endif #define PIF_TIMEOUT (180 * HZ) #define DPRAM_INIT_TIMEOUT (30 * HZ) +#if defined(CONFIG_MACH_M0_DUOSCTC) || defined(CONFIG_MACH_M0_GRANDECTC) || \ + defined(CONFIG_MACH_T0_CHN_CTC) +static void mdm6600_vbus_on(void) +{ + struct regulator *regulator; + + pr_info("[MSM] <%s>\n", __func__); + +#if defined(CONFIG_MACH_T0_CHN_CTC) + if (system_rev == 4) + regulator = regulator_get(NULL, "vcc_1.8v_lcd"); + else + regulator = regulator_get(NULL, "vcc_1.8v_usb"); +#else + regulator = regulator_get(NULL, "vusbhub_osc_1.8v"); +#endif + if (IS_ERR(regulator)) { + pr_err("[MSM] error getting regulator_get <%s>\n", __func__); + return ; + } + regulator_enable(regulator); + regulator_put(regulator); + + pr_info("[MSM] <%s> enable\n", __func__); +} + +static void mdm6600_vbus_off(void) +{ + struct regulator *regulator; + + pr_info("[MSM] <%s>\n", __func__); + +#if defined(CONFIG_MACH_T0_CHN_CTC) + if (system_rev == 4) + regulator = regulator_get(NULL, "vcc_1.8v_lcd"); + else + regulator = regulator_get(NULL, "vcc_1.8v_usb"); +#else + regulator = regulator_get(NULL, "vusbhub_osc_1.8v"); +#endif + if (IS_ERR(regulator)) { + pr_err("[MSM] error getting regulator_get <%s>\n", __func__); + return ; + } + regulator_disable(regulator); + regulator_put(regulator); + + pr_info("[MSM] <%s> disable\n", __func__); +} +#endif + static int mdm6600_on(struct modem_ctl *mc) { + struct link_device *ld = get_current_link(mc->iod); + pr_info("[MSM] <%s>\n", __func__); if (!mc->gpio_reset_req_n || !mc->gpio_cp_reset @@ -274,20 +332,28 @@ static int mdm6600_on(struct modem_ctl *mc) return -ENXIO; } - gpio_set_value(mc->gpio_reset_req_n, 1); - - gpio_set_value(mc->gpio_cp_reset, 1); - msleep(30); + gpio_set_value(mc->gpio_pda_active, 0); gpio_set_value(mc->gpio_cp_on, 1); msleep(500); + gpio_set_value(mc->gpio_reset_req_n, 1); + msleep(50); + + gpio_set_value(mc->gpio_cp_reset, 1); + msleep(50); + gpio_set_value(mc->gpio_cp_on, 0); msleep(500); gpio_set_value(mc->gpio_pda_active, 1); +#if defined(CONFIG_LINK_DEVICE_PLD) + gpio_set_value(mc->gpio_fpga_cs_n, 1); +#endif + mc->iod->modem_state_changed(mc->iod, STATE_BOOTING); + ld->mode = LINK_MODE_BOOT; return 0; } @@ -302,8 +368,11 @@ static int mdm6600_off(struct modem_ctl *mc) } gpio_set_value(mc->gpio_cp_reset, 0); + gpio_set_value(mc->gpio_reset_req_n, 0); gpio_set_value(mc->gpio_cp_on, 0); + msleep(200); + mc->iod->modem_state_changed(mc->iod, STATE_OFFLINE); return 0; @@ -312,26 +381,46 @@ static int mdm6600_off(struct modem_ctl *mc) static int mdm6600_reset(struct modem_ctl *mc) { int ret = 0; + struct link_device *ld = get_current_link(mc->iod); pr_info("[MSM] <%s>\n", __func__); - ret = mdm6600_off(mc); - if (ret) + if (!mc->gpio_reset_req_n || !mc->gpio_cp_reset + || !mc->gpio_cp_on || !mc->gpio_pda_active) { + pr_err("[MSM] no gpio data\n"); return -ENXIO; + } + + gpio_set_value(mc->gpio_cp_reset, 0); + gpio_set_value(mc->gpio_reset_req_n, 0); + gpio_set_value(mc->gpio_cp_on, 0); -#if 0 msleep(100); -#endif - ret = mdm6600_on(mc); - if (ret) - return -ENXIO; + gpio_set_value(mc->gpio_cp_on, 1); + msleep(300); + + gpio_set_value(mc->gpio_reset_req_n, 1); + msleep(50); + + gpio_set_value(mc->gpio_cp_reset, 1); + msleep(50); + + gpio_set_value(mc->gpio_cp_on, 0); + msleep(100); + + gpio_set_value(mc->gpio_pda_active, 1); + + mc->iod->modem_state_changed(mc->iod, STATE_BOOTING); + ld->mode = LINK_MODE_BOOT; return 0; } static int mdm6600_boot_on(struct modem_ctl *mc) { + struct regulator *regulator; + pr_info("[MSM] <%s>\n", __func__); if (!mc->gpio_flm_uart_sel) { @@ -339,62 +428,106 @@ static int mdm6600_boot_on(struct modem_ctl *mc) return -ENXIO; } - pr_info("[MSM] <%s> %s\n", __func__, "USB_BOOT_EN initializing"); +#if defined(CONFIG_MACH_M0_DUOSCTC) || defined(CONFIG_MACH_T0_CHN_CTC) + mdm6600_vbus_on(); +#elif defined(CONFIG_MACH_M0_GRANDECTC) + if (system_rev >= 14) + mdm6600_vbus_on(); +#endif + pr_info("[MSM] <%s> %s\n", __func__, "USB_BOOT_EN initializing"); if (system_rev < 11) { gpio_direction_output(GPIO_USB_BOOT_EN, 0); s3c_gpio_setpull(GPIO_USB_BOOT_EN, S3C_GPIO_PULL_NONE); gpio_set_value(GPIO_USB_BOOT_EN, 0); + gpio_direction_output(GPIO_BOOT_SW_SEL, 0); + s3c_gpio_setpull(GPIO_BOOT_SW_SEL, S3C_GPIO_PULL_NONE); + gpio_set_value(GPIO_BOOT_SW_SEL, 0); + + msleep(100); + gpio_direction_output(GPIO_USB_BOOT_EN, 1); gpio_set_value(GPIO_USB_BOOT_EN, 1); pr_info("[MSM] <%s> USB_BOOT_EN:[%d]\n", __func__, gpio_get_value(GPIO_USB_BOOT_EN)); + + gpio_direction_output(GPIO_BOOT_SW_SEL, 1); + gpio_set_value(GPIO_BOOT_SW_SEL, 1); + + pr_info("[MSM] <%s> BOOT_SW_SEL : [%d]\n", __func__, + gpio_get_value(GPIO_BOOT_SW_SEL)); } else if (system_rev == 11) { gpio_direction_output(GPIO_USB_BOOT_EN, 0); s3c_gpio_setpull(GPIO_USB_BOOT_EN, S3C_GPIO_PULL_NONE); gpio_set_value(GPIO_USB_BOOT_EN, 0); + gpio_direction_output(GPIO_USB_BOOT_EN_REV06, 0); + s3c_gpio_setpull(GPIO_USB_BOOT_EN_REV06, S3C_GPIO_PULL_NONE); + gpio_set_value(GPIO_USB_BOOT_EN_REV06, 0); + + msleep(100); + gpio_direction_output(GPIO_USB_BOOT_EN, 1); gpio_set_value(GPIO_USB_BOOT_EN, 1); pr_info("[MSM] <%s> USB_BOOT_EN:[%d]\n", __func__, gpio_get_value(GPIO_USB_BOOT_EN)); - gpio_direction_output(GPIO_USB_BOOT_EN_REV06, 0); - s3c_gpio_setpull(GPIO_USB_BOOT_EN_REV06, S3C_GPIO_PULL_NONE); - gpio_set_value(GPIO_USB_BOOT_EN_REV06, 0); - gpio_direction_output(GPIO_USB_BOOT_EN_REV06, 1); gpio_set_value(GPIO_USB_BOOT_EN_REV06, 1); - pr_info("[MSM] <%s> USB_BOOT_EN:[%d]\n", __func__, - gpio_get_value(GPIO_USB_BOOT_EN_REV06)); + pr_info("[MSM(%d)] <%s> USB_BOOT_EN:[%d]\n", system_rev, + __func__, gpio_get_value(GPIO_USB_BOOT_EN_REV06)); + + gpio_direction_output(GPIO_BOOT_SW_SEL, 0); + s3c_gpio_setpull(GPIO_BOOT_SW_SEL, S3C_GPIO_PULL_NONE); + gpio_set_value(GPIO_BOOT_SW_SEL, 0); + + gpio_direction_output(GPIO_BOOT_SW_SEL_REV06, 0); + s3c_gpio_setpull(GPIO_BOOT_SW_SEL_REV06, S3C_GPIO_PULL_NONE); + gpio_set_value(GPIO_BOOT_SW_SEL_REV06, 0); + + msleep(100); + + gpio_direction_output(GPIO_BOOT_SW_SEL, 1); + gpio_set_value(GPIO_BOOT_SW_SEL, 1); + + pr_info("[MSM] <%s> BOOT_SW_SEL : [%d]\n", __func__, + gpio_get_value(GPIO_BOOT_SW_SEL)); + + gpio_direction_output(GPIO_BOOT_SW_SEL_REV06, 1); + gpio_set_value(GPIO_BOOT_SW_SEL_REV06, 1); + + pr_info("[MSM(%d)] <%s> BOOT_SW_SEL : [%d]\n", system_rev, + __func__, gpio_get_value(GPIO_BOOT_SW_SEL_REV06)); } else { /* system_rev>11 */ gpio_direction_output(GPIO_USB_BOOT_EN_REV06, 0); s3c_gpio_setpull(GPIO_USB_BOOT_EN_REV06, S3C_GPIO_PULL_NONE); gpio_set_value(GPIO_USB_BOOT_EN_REV06, 0); + gpio_direction_output(GPIO_BOOT_SW_SEL_REV06, 0); + s3c_gpio_setpull(GPIO_BOOT_SW_SEL_REV06, S3C_GPIO_PULL_NONE); + gpio_set_value(GPIO_BOOT_SW_SEL_REV06, 0); + + msleep(100); + gpio_direction_output(GPIO_USB_BOOT_EN_REV06, 1); gpio_set_value(GPIO_USB_BOOT_EN_REV06, 1); pr_info("[MSM] <%s> USB_BOOT_EN:[%d]\n", __func__, gpio_get_value(GPIO_USB_BOOT_EN_REV06)); - } - - gpio_direction_output(mc->gpio_flm_uart_sel, 0); - s3c_gpio_setpull(mc->gpio_flm_uart_sel, S3C_GPIO_PULL_NONE); - gpio_set_value(mc->gpio_flm_uart_sel, 0); - - gpio_direction_output(mc->gpio_flm_uart_sel, 1); - gpio_set_value(mc->gpio_flm_uart_sel, 1); + gpio_direction_output(GPIO_BOOT_SW_SEL_REV06, 1); + gpio_set_value(GPIO_BOOT_SW_SEL_REV06, 1); pr_info("[MSM] <%s> BOOT_SW_SEL : [%d]\n", __func__, - gpio_get_value(mc->gpio_flm_uart_sel)); + gpio_get_value(GPIO_BOOT_SW_SEL_REV06)); + + } mc->iod->modem_state_changed(mc->iod, STATE_BOOTING); @@ -405,11 +538,22 @@ static int mdm6600_boot_off(struct modem_ctl *mc) { pr_info("[MSM] <%s>\n", __func__); - if (!mc->gpio_flm_uart_sel || !mc->gpio_flm_uart_sel_rev06) { + if (!mc->gpio_flm_uart_sel +#if defined(CONFIG_MACH_M0_CTC) + || !mc->gpio_flm_uart_sel_rev06 +#endif + ) { pr_err("[MSM] no gpio data\n"); return -ENXIO; } +#if defined(CONFIG_MACH_M0_DUOSCTC) || defined(CONFIG_MACH_T0_CHN_CTC) + mdm6600_vbus_off(); +#elif defined(CONFIG_MACH_M0_GRANDECTC) + if (system_rev >= 14) + mdm6600_vbus_off(); +#endif + if (system_rev < 11) { gpio_direction_output(GPIO_USB_BOOT_EN, 0); s3c_gpio_setpull(GPIO_USB_BOOT_EN, S3C_GPIO_PULL_NONE); @@ -422,19 +566,23 @@ static int mdm6600_boot_off(struct modem_ctl *mc) gpio_direction_output(GPIO_USB_BOOT_EN, 0); s3c_gpio_setpull(GPIO_USB_BOOT_EN, S3C_GPIO_PULL_NONE); gpio_set_value(GPIO_USB_BOOT_EN, 0); - gpio_direction_output(GPIO_USB_BOOT_EN_REV06, 0); - s3c_gpio_setpull(GPIO_USB_BOOT_EN_REV06, S3C_GPIO_PULL_NONE); - gpio_set_value(GPIO_USB_BOOT_EN_REV06, 0); gpio_direction_output(GPIO_BOOT_SW_SEL, 0); s3c_gpio_setpull(GPIO_BOOT_SW_SEL, S3C_GPIO_PULL_NONE); gpio_set_value(GPIO_BOOT_SW_SEL, 0); +#if defined(CONFIG_MACH_M0_CTC) + gpio_direction_output(GPIO_USB_BOOT_EN_REV06, 0); + s3c_gpio_setpull(GPIO_USB_BOOT_EN_REV06, S3C_GPIO_PULL_NONE); + gpio_set_value(GPIO_USB_BOOT_EN_REV06, 0); + gpio_direction_output(GPIO_BOOT_SW_SEL_REV06, 0); s3c_gpio_setpull(GPIO_BOOT_SW_SEL_REV06, S3C_GPIO_PULL_NONE); gpio_set_value(GPIO_BOOT_SW_SEL_REV06, 0); +#endif } else { /* system_rev>11 */ +#if defined(CONFIG_MACH_M0_CTC) gpio_direction_output(GPIO_USB_BOOT_EN_REV06, 0); s3c_gpio_setpull(GPIO_USB_BOOT_EN_REV06, S3C_GPIO_PULL_NONE); gpio_set_value(GPIO_USB_BOOT_EN_REV06, 0); @@ -442,9 +590,10 @@ static int mdm6600_boot_off(struct modem_ctl *mc) gpio_direction_output(GPIO_BOOT_SW_SEL_REV06, 0); s3c_gpio_setpull(GPIO_BOOT_SW_SEL_REV06, S3C_GPIO_PULL_NONE); gpio_set_value(GPIO_BOOT_SW_SEL_REV06, 0); - +#endif } +#if defined(CONFIG_MACH_M0_CTC) if (max7693_muic_cp_usb_state()) { msleep(30); gpio_direction_output(GPIO_USB_BOOT_EN, 1); @@ -454,8 +603,29 @@ static int mdm6600_boot_off(struct modem_ctl *mc) s3c_gpio_setpull(GPIO_USB_BOOT_EN_REV06, S3C_GPIO_PULL_NONE); gpio_set_value(GPIO_USB_BOOT_EN_REV06, 1); } +#endif + + gpio_set_value(GPIO_BOOT_SW_SEL, 0); + + return 0; +} + + +static int mdm6600_force_crash_exit(struct modem_ctl *mc) +{ + pr_info("[MSM] <%s>\n", __func__); + + if (!mc->gpio_cp_reset || !mc->gpio_cp_on) { + pr_err("[MSM] no gpio data\n"); + return -ENXIO; + } + + s3c_gpio_cfgpin(mc->gpio_cp_dump_int, S3C_GPIO_OUTPUT); + gpio_direction_output(mc->gpio_cp_dump_int, 1); - gpio_set_value(mc->gpio_flm_uart_sel, 0); + gpio_set_value(mc->gpio_cp_reset, 0); + gpio_set_value(mc->gpio_cp_on, 0); + gpio_set_value(mc->gpio_cp_reset, 1); return 0; } @@ -466,16 +636,20 @@ static irqreturn_t phone_active_irq_handler(int irq, void *arg) int phone_reset = 0; int phone_active = 0; int phone_state = 0; + int cp_dump_int = 0; - if (!mc->gpio_cp_reset || !mc->gpio_phone_active) { + if (!mc->gpio_cp_reset || + !mc->gpio_phone_active || !mc->gpio_cp_dump_int) { pr_err("[MSM] no gpio data\n"); return IRQ_HANDLED; } phone_reset = gpio_get_value(mc->gpio_cp_reset); phone_active = gpio_get_value(mc->gpio_phone_active); - pr_info("[MSM] <%s> phone_reset = %d, phone_active = %d\n", - __func__, phone_reset, phone_active); + cp_dump_int = gpio_get_value(mc->gpio_cp_dump_int); + + pr_info("[MSM] <%s> phone_reset=%d, phone_active=%d, cp_dump_int=%d\n", + __func__, phone_reset, phone_active, cp_dump_int); if (phone_reset && phone_active) { phone_state = STATE_ONLINE; @@ -483,7 +657,10 @@ static irqreturn_t phone_active_irq_handler(int irq, void *arg) mc->iod->modem_state_changed(mc->iod, phone_state); } else if (phone_reset && !phone_active) { if (mc->phone_state == STATE_ONLINE) { - phone_state = STATE_CRASH_EXIT; + if (cp_dump_int) + phone_state = STATE_CRASH_EXIT; + else + phone_state = STATE_CRASH_RESET; if (mc->iod && mc->iod->modem_state_changed) mc->iod->modem_state_changed(mc->iod, phone_state); @@ -504,6 +681,22 @@ static irqreturn_t phone_active_irq_handler(int irq, void *arg) return IRQ_HANDLED; } +#if defined(CONFIG_SIM_DETECT) +static irqreturn_t sim_detect_irq_handler(int irq, void *_mc) +{ + struct modem_ctl *mc = (struct modem_ctl *)_mc; + + pr_info("[MSM] <%s> gpio_sim_detect = %d\n", + __func__, gpio_get_value(mc->gpio_sim_detect)); + + if (mc->iod && mc->iod->sim_state_changed) + mc->iod->sim_state_changed(mc->iod, + !gpio_get_value(mc->gpio_sim_detect)); + + return IRQ_HANDLED; +} +#endif + static void mdm6600_get_ops(struct modem_ctl *mc) { mc->ops.modem_on = mdm6600_on; @@ -511,6 +704,7 @@ static void mdm6600_get_ops(struct modem_ctl *mc) mc->ops.modem_reset = mdm6600_reset; mc->ops.modem_boot_on = mdm6600_boot_on; mc->ops.modem_boot_off = mdm6600_boot_off; + mc->ops.modem_force_crash_exit = mdm6600_force_crash_exit; } int mdm6600_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata) @@ -525,10 +719,15 @@ int mdm6600_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata) mc->gpio_phone_active = pdata->gpio_phone_active; mc->gpio_cp_dump_int = pdata->gpio_cp_dump_int; mc->gpio_flm_uart_sel = pdata->gpio_flm_uart_sel; -#if 1 +#if defined(CONFIG_MACH_M0_CTC) mc->gpio_flm_uart_sel_rev06 = pdata->gpio_flm_uart_sel_rev06; #endif mc->gpio_cp_warm_reset = pdata->gpio_cp_warm_reset; + mc->gpio_sim_detect = pdata->gpio_sim_detect; + +#if defined(CONFIG_LINK_DEVICE_PLD) + mc->gpio_fpga_cs_n = pdata->gpio_fpga2_cs_n; +#endif gpio_set_value(mc->gpio_cp_reset, 0); gpio_set_value(mc->gpio_cp_on, 0); @@ -557,6 +756,36 @@ int mdm6600_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata) return ret; } +#if defined(CONFIG_SIM_DETECT) + mc->irq_sim_detect = platform_get_irq_byname(pdev, "sim_irq"); + pr_info("[MSM] <%s> SIM_DECTCT IRQ# = %d\n", + __func__, mc->irq_sim_detect); + + if (mc->irq_sim_detect) { + ret = request_irq(mc->irq_sim_detect, sim_detect_irq_handler, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + "msm_sim_detect", mc); + if (ret) { + mif_err("[MSM] failed to request_irq: %d\n", ret); + mc->sim_state.online = false; + mc->sim_state.changed = false; + return ret; + } + + ret = enable_irq_wake(mc->irq_sim_detect); + if (ret) { + mif_err("[MSM] failed to enable_irq_wake: %d\n", ret); + free_irq(mc->irq_sim_detect, mc); + mc->sim_state.online = false; + mc->sim_state.changed = false; + return ret; + } + + /* initialize sim_state => insert: gpio=0, remove: gpio=1 */ + mc->sim_state.online = !gpio_get_value(mc->gpio_sim_detect); + } +#endif + return ret; } #endif diff --git a/drivers/misc/modem_if/modem_modemctl_device_sprd8803.c b/drivers/misc/modem_if/modem_modemctl_device_sprd8803.c new file mode 100644 index 0000000..9db864de --- /dev/null +++ b/drivers/misc/modem_if/modem_modemctl_device_sprd8803.c @@ -0,0 +1,230 @@ +/* /linux/drivers/misc/modem_if/modem_modemctl_device_sprd8803.c + * + * Copyright (C) 2010 Google, Inc. + * Copyright (C) 2010 Samsung Electronics. + * + * 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/irq.h> +#include <linux/interrupt.h> +#include <linux/gpio.h> +#include <linux/delay.h> +#include <linux/platform_device.h> +#include <linux/cma.h> +#include <plat/devs.h> +#include <linux/platform_data/modem.h> +#include "modem_prj.h" +#include <plat/gpio-cfg.h> + +int sprd_boot_done; +extern int spi_thread_restart(void); + +static int sprd8803_on(struct modem_ctl *mc) +{ + pr_debug("[MODEM_IF] %s\n", __func__); + + if (!mc->gpio_cp_on || !mc->gpio_pda_active) { + pr_err("[MODEM_IF] no gpio data\n"); + return -ENXIO; + } + + s3c_gpio_cfgpin(EXYNOS4_GPA1(4), S3C_GPIO_SFN(2)); + s3c_gpio_cfgpin(EXYNOS4_GPA1(5), S3C_GPIO_SFN(2)); + +#ifdef CONFIG_SEC_DUAL_MODEM_MODE + gpio_set_value(mc->gpio_sim_io_sel, 1); + gpio_set_value(mc->gpio_cp_ctrl1, 0); + gpio_set_value(mc->gpio_cp_ctrl2, 1); +#endif + gpio_set_value(mc->gpio_cp_on, 0); + gpio_set_value(mc->gpio_pda_active, 0); + msleep(100); + gpio_set_value(mc->gpio_cp_on, 1); + gpio_set_value(mc->gpio_pda_active, 1); + + mc->phone_state = STATE_BOOTING; + + return 0; +} + +static int sprd8803_off(struct modem_ctl *mc) +{ + pr_debug("[MODEM_IF] %s\n", __func__); + + if (!mc->gpio_cp_on) { + mif_err("no gpio data\n"); + return -ENXIO; + } + + gpio_set_value(mc->gpio_cp_on, 0); + + mc->phone_state = STATE_OFFLINE; + + return 0; +} + +static int sprd8803_reset(struct modem_ctl *mc) +{ + pr_debug("[MODEM_IF] %s\n", __func__); + + spi_thread_restart(); + + return 0; +} + +static int sprd8803_boot_on(struct modem_ctl *mc) +{ + pr_debug("[MODEM_IF] %s %d\n", __func__, sprd_boot_done); + return sprd_boot_done; +} + +static int sprd8803_boot_off(struct modem_ctl *mc) +{ + pr_debug("[MODEM_IF] %s\n", __func__); + spi_sema_init(); + return 0; +} + +static int sprd8803_dump_reset(struct modem_ctl *mc) +{ + pr_debug("[MODEM_IF] %s\n", __func__); + + if (!mc->gpio_ap_cp_int2) + return -ENXIO; + + gpio_set_value(mc->gpio_ap_cp_int2, 0); + mc->phone_state = STATE_OFFLINE; + msleep(100); + gpio_set_value(mc->gpio_ap_cp_int2, 1); + + return 0; +} + +static irqreturn_t phone_active_irq_handler(int irq, void *_mc) +{ + int phone_reset = 0; + int phone_active_value = 0; + int cp_dump_value = 0; + int phone_state = 0; + struct modem_ctl *mc = (struct modem_ctl *)_mc; + + disable_irq_nosync(mc->irq_phone_active); + disable_irq_nosync(gpio_to_irq(mc->gpio_cp_dump_int)); + + if (!mc->gpio_phone_active || + !mc->gpio_cp_dump_int) { + pr_err("[MODEM_IF] no gpio data\n"); + goto exit; + } + + if (!sprd_boot_done) + goto exit; + + phone_active_value = gpio_get_value(mc->gpio_phone_active); + cp_dump_value = gpio_get_value(mc->gpio_cp_dump_int); + + pr_err("PA EVENT : pa=%d, cp_dump=%d\n", + phone_active_value, cp_dump_value); + + if (phone_active_value) + phone_state = STATE_ONLINE; + else + phone_state = STATE_OFFLINE; + + if (phone_active_value && cp_dump_value) + phone_state = STATE_CRASH_EXIT; + + if (mc->iod && mc->iod->modem_state_changed) + mc->iod->modem_state_changed(mc->iod, phone_state); + + if (mc->bootd && mc->bootd->modem_state_changed) + mc->bootd->modem_state_changed(mc->bootd, phone_state); + +exit: + enable_irq(mc->irq_phone_active); + enable_irq(gpio_to_irq(mc->gpio_cp_dump_int)); + + return IRQ_HANDLED; +} + +static void sprd8803_get_ops(struct modem_ctl *mc) +{ + mc->ops.modem_on = sprd8803_on; + mc->ops.modem_off = sprd8803_off; + mc->ops.modem_reset = sprd8803_reset; + mc->ops.modem_boot_on = sprd8803_boot_on; + mc->ops.modem_boot_off = sprd8803_boot_off; + mc->ops.modem_dump_reset = sprd8803_dump_reset; + mc->ops.modem_force_crash_exit = sprd8803_dump_reset; +} + +int sprd8803_init_modemctl_device(struct modem_ctl *mc, + struct modem_data *pdata) +{ + int ret = 0; + int irq_cp_dump_int; + struct platform_device *pdev; + + mc->gpio_cp_on = pdata->gpio_cp_on; + mc->gpio_pda_active = pdata->gpio_pda_active; + mc->gpio_phone_active = pdata->gpio_phone_active; + mc->gpio_cp_dump_int = pdata->gpio_cp_dump_int; + mc->gpio_ap_cp_int1 = pdata->gpio_ap_cp_int1; + mc->gpio_ap_cp_int2 = pdata->gpio_ap_cp_int2; + +#ifdef CONFIG_SEC_DUAL_MODEM_MODE + mc->gpio_sim_io_sel = pdata->gpio_sim_io_sel; + mc->gpio_cp_ctrl1 = pdata->gpio_cp_ctrl1; + mc->gpio_cp_ctrl2 = pdata->gpio_cp_ctrl2; +#endif + + + pdev = to_platform_device(mc->dev); + mc->irq_phone_active = gpio_to_irq(mc->gpio_phone_active); + irq_cp_dump_int = gpio_to_irq(mc->gpio_cp_dump_int); + + sprd8803_get_ops(mc); + + ret = request_irq(mc->irq_phone_active, phone_active_irq_handler, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + "phone_active", mc); + if (ret) { + pr_err("[MODEM_IF] %s: failed to request_irq:%d\n", + __func__, ret); + return ret; + } + + ret = enable_irq_wake(mc->irq_phone_active); + if (ret) { + pr_err("[MODEM_IF] %s: failed to enable_irq_wake:%d\n", + __func__, ret); + free_irq(mc->irq_phone_active, mc); + } + + ret = request_irq(irq_cp_dump_int, phone_active_irq_handler, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + "cp_dump_int", mc); + if (ret) { + pr_err("[MODEM_IF] %s: failed to request_irq:%d\n", + __func__, ret); + return ret; + } + + ret = enable_irq_wake(irq_cp_dump_int); + if (ret) { + pr_err("[MODEM_IF] %s: failed to enable_irq_wake:%d\n", + __func__, ret); + free_irq(irq_cp_dump_int, mc); + } + return ret; +} diff --git a/drivers/misc/modem_if/modem_modemctl_device_xmm6262.c b/drivers/misc/modem_if/modem_modemctl_device_xmm6262.c index 10b0811..4d0b69c 100644 --- a/drivers/misc/modem_if/modem_modemctl_device_xmm6262.c +++ b/drivers/misc/modem_if/modem_modemctl_device_xmm6262.c @@ -39,6 +39,12 @@ static int xmm6262_on(struct modem_ctl *mc) if (mc->gpio_revers_bias_clear) mc->gpio_revers_bias_clear(); +#ifdef CONFIG_SEC_DUAL_MODEM_MODE + gpio_set_value(mc->gpio_sim_io_sel, 0); + gpio_set_value(mc->gpio_cp_ctrl1, 1); + gpio_set_value(mc->gpio_cp_ctrl2, 0); +#endif + /* TODO */ gpio_set_value(mc->gpio_reset_req_n, 0); gpio_set_value(mc->gpio_cp_on, 0); @@ -137,15 +143,19 @@ static irqreturn_t phone_active_irq_handler(int irq, void *_mc) mif_info("PA EVENT : reset =%d, pa=%d, cp_dump=%d\n", phone_reset, phone_active_value, cp_dump_value); - if (phone_reset && phone_active_value) + if (phone_reset && phone_active_value) { phone_state = STATE_BOOTING; - else if (phone_reset && !phone_active_value) { - if (cp_dump_value) - phone_state = STATE_CRASH_EXIT; - else - phone_state = STATE_CRASH_RESET; - } else + } else if (mc->dev->power.is_suspended && !phone_active_value) { + /*fixing dpm timeout by port2 resume retry*/ + mif_err("CP reset while dpm resume\n"); + xmm6262_off(mc); + phone_state = STATE_CRASH_RESET; + } else if (phone_reset && !phone_active_value) { + phone_state = + (cp_dump_value) ? STATE_CRASH_EXIT : STATE_CRASH_RESET; + } else { phone_state = STATE_OFFLINE; + } if (mc->iod && mc->iod->modem_state_changed) mc->iod->modem_state_changed(mc->iod, phone_state); @@ -168,7 +178,8 @@ static irqreturn_t sim_detect_irq_handler(int irq, void *_mc) if (mc->iod && mc->iod->sim_state_changed) mc->iod->sim_state_changed(mc->iod, - !gpio_get_value(mc->gpio_sim_detect)); + gpio_get_value(mc->gpio_sim_detect) == mc->sim_polarity + ); return IRQ_HANDLED; } @@ -196,10 +207,17 @@ int xmm6262_init_modemctl_device(struct modem_ctl *mc, mc->gpio_flm_uart_sel = pdata->gpio_flm_uart_sel; mc->gpio_cp_warm_reset = pdata->gpio_cp_warm_reset; mc->gpio_sim_detect = pdata->gpio_sim_detect; + mc->sim_polarity = pdata->sim_polarity; mc->gpio_revers_bias_clear = pdata->gpio_revers_bias_clear; mc->gpio_revers_bias_restore = pdata->gpio_revers_bias_restore; +#ifdef CONFIG_SEC_DUAL_MODEM_MODE + mc->gpio_sim_io_sel = pdata->gpio_sim_io_sel; + mc->gpio_cp_ctrl1 = pdata->gpio_cp_ctrl1; + mc->gpio_cp_ctrl2 = pdata->gpio_cp_ctrl2; +#endif + pdev = to_platform_device(mc->dev); mc->irq_phone_active = gpio_to_irq(mc->gpio_phone_active); @@ -241,7 +259,8 @@ int xmm6262_init_modemctl_device(struct modem_ctl *mc, } /* initialize sim_state => insert: gpio=0, remove: gpio=1 */ - mc->sim_state.online = !gpio_get_value(mc->gpio_sim_detect); + mc->sim_state.online = + gpio_get_value(mc->gpio_sim_detect) == mc->sim_polarity; } return ret; diff --git a/drivers/misc/modem_if/modem_prj.h b/drivers/misc/modem_if/modem_prj.h index 464370f..f85596f 100644 --- a/drivers/misc/modem_if/modem_prj.h +++ b/drivers/misc/modem_if/modem_prj.h @@ -23,6 +23,8 @@ #include <linux/wakelock.h> #include <linux/rbtree.h> #include <linux/spinlock.h> +#include <linux/cdev.h> +#include <linux/types.h> #define MAX_CPINFO_SIZE 512 @@ -38,7 +40,7 @@ #define IOCTL_MODEM_RESET _IO('o', 0x21) #define IOCTL_MODEM_BOOT_ON _IO('o', 0x22) #define IOCTL_MODEM_BOOT_OFF _IO('o', 0x23) -#define IOCTL_MODEM_START _IO('o', 0x24) +#define IOCTL_MODEM_BOOT_DONE _IO('o', 0x24) #define IOCTL_MODEM_PROTOCOL_SUSPEND _IO('o', 0x25) #define IOCTL_MODEM_PROTOCOL_RESUME _IO('o', 0x26) @@ -56,6 +58,10 @@ #define IOCTL_MODEM_CP_UPLOAD _IO('o', 0x35) #define IOCTL_MODEM_DUMP_RESET _IO('o', 0x36) +#if defined(CONFIG_SEC_DUAL_MODEM_MODE) +#define IOCTL_MODEM_SWITCH_MODEM _IO('o', 0x37) +#endif + #define IOCTL_DPRAM_SEND_BOOT _IO('o', 0x40) #define IOCTL_DPRAM_INIT_STATUS _IO('o', 0x43) @@ -68,6 +74,10 @@ #define IOCTL_DPRAM_PHONE_UPLOAD_STEP1 _IO('o', 0xde) #define IOCTL_DPRAM_PHONE_UPLOAD_STEP2 _IO('o', 0xdf) +/* ioctl command for IPC Logger */ +#define IOCTL_MIF_LOG_DUMP _IO('o', 0x51) +#define IOCTL_MIF_DPRAM_DUMP _IO('o', 0x52) + /* modem status */ #define MODEM_OFF 0 #define MODEM_CRASHED 1 @@ -90,6 +100,13 @@ #define SOURCE_MAC_ADDR {0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC} +/* loopback: CP -> AP -> CP */ +#define CP2AP_LOOPBACK_CHANNEL 30 + +/* ip loopback */ +#define RMNET0_CH_ID 10 +#define DATA_LOOPBACK_CHANNEL 31 + /* Debugging features */ #define MAX_MIF_LOG_PATH_LEN 128 #define MAX_MIF_LOG_FILE_SIZE 0x800000 /* 8 MB */ @@ -129,6 +146,7 @@ struct dpram_irq_buff { unsigned int2cp; }; +/* Not use */ struct mif_event_buff { char time[MAX_MIF_TIME_LEN]; @@ -164,6 +182,9 @@ enum modem_state { STATE_LOADER_DONE, STATE_SIM_ATTACH, STATE_SIM_DETACH, +#if defined(CONFIG_SEC_DUAL_MODEM_MODE) + STATE_MODEM_SWITCH, +#endif }; enum com_state { @@ -175,9 +196,9 @@ enum com_state { }; enum link_mode { - LINK_MODE_INVALID = 0, - LINK_MODE_IPC, + LINK_MODE_OFFLINE = 0, LINK_MODE_BOOT, + LINK_MODE_IPC, LINK_MODE_DLOAD, LINK_MODE_ULOAD, }; @@ -247,6 +268,8 @@ struct sipc_fmt_hdr { #define SIPC5_CTL_OFFSET 4 #define SIPC5_CH_ID_RAW_0 0 +#define SIPC5_CH_ID_PDP_0 10 +#define SIPC5_CH_ID_PDP_LAST 24 #define SIPC5_CH_ID_FMT_0 235 #define SIPC5_CH_ID_RFS_0 245 #define SIPC5_CH_ID_MAX 255 @@ -265,7 +288,10 @@ struct sipc5_link_hdr { u8 cfg; u8 ch; u16 len; - u8 ctl; + union { + u8 ctl; + u16 ext_len; + }; } __packed; struct sipc5_frame_data { @@ -280,7 +306,6 @@ struct sipc5_frame_data { /* Frame configuration set by header analysis */ bool padding; - bool ext_fld; bool ctl_fld; bool ext_len; @@ -303,24 +328,6 @@ struct sipc5_frame_data { u8 hdr[SIPC5_MAX_HEADER_SIZE]; }; -static inline unsigned sipc5_get_hdr_size(u8 cfg) -{ - if (cfg & SIPC5_EXT_FIELD_EXIST) { - if (cfg & SIPC5_CTL_FIELD_EXIST) - return SIPC5_HEADER_SIZE_WITH_CTL_FLD; - else - return SIPC5_HEADER_SIZE_WITH_EXT_LEN; - } else { - return SIPC5_MIN_HEADER_SIZE; - } -} - -static inline unsigned sipc5_calc_padding_size(unsigned len) -{ - unsigned residue = len & 0x3; - return residue ? (4 - residue) : 0; -} - struct vnet { struct io_device *iod; }; @@ -342,7 +349,9 @@ struct skbuff_private { struct io_device *iod; struct link_device *ld; struct io_device *real_iod; /* for rx multipdp */ -}; + u8 ch_id; + u8 control; +} __packed; static inline struct skbuff_private *skbpriv(struct sk_buff *skb) { @@ -350,25 +359,6 @@ static inline struct skbuff_private *skbpriv(struct sk_buff *skb) return (struct skbuff_private *)&skb->cb; } -/** rx_alloc_skb - allocate an skbuff and set skb's iod, ld - * @length: length to allocate - * @gfp_mask: get_free_pages mask, passed to alloc_skb - * @iod: struct io_device * - * @ld: struct link_device * - * - * %NULL is returned if there is no free memory. - */ -static inline struct sk_buff *rx_alloc_skb(unsigned int length, - gfp_t gfp_mask, struct io_device *iod, struct link_device *ld) -{ - struct sk_buff *skb = alloc_skb(length, gfp_mask); - if (likely(skb)) { - skbpriv(skb)->iod = iod; - skbpriv(skb)->ld = ld; - } - return skb; -} - struct io_device { /* rb_tree node for an io device */ struct rb_node node_chan; @@ -398,9 +388,6 @@ struct io_device { /* SIPC version */ enum sipc_ver ipc_version; - /* Tx header buffer */ - struct sipc5_frame_data meta_frame; - /* Rx queue of sk_buff */ struct sk_buff_head sk_rx_q; @@ -418,6 +405,8 @@ struct io_device { /* called from linkdevice when a packet arrives for this iodevice */ int (*recv)(struct io_device *iod, struct link_device *ld, const char *data, unsigned int len); + int (*recv_skb)(struct io_device *iod, struct link_device *ld, + struct sk_buff *skb); /* inform the IO device that the modem is now online or offline or * crashing or whatever... @@ -428,6 +417,7 @@ struct io_device { void (*sim_state_changed)(struct io_device *iod, bool sim_online); struct modem_ctl *mc; + struct modem_shared *msd; struct wake_lock wakelock; long waketime; @@ -463,6 +453,9 @@ struct link_device { /* Modem control */ struct modem_ctl *mc; + /* Modem shared data */ + struct modem_shared *msd; + /* Operation mode of the link device */ enum link_mode mode; @@ -518,26 +511,68 @@ struct link_device { unsigned cmd, unsigned long _arg); }; +/** rx_alloc_skb - allocate an skbuff and set skb's iod, ld + * @length: length to allocate + * @iod: struct io_device * + * @ld: struct link_device * + * + * %NULL is returned if there is no free memory. + */ +static inline struct sk_buff *rx_alloc_skb(unsigned int length, + struct io_device *iod, struct link_device *ld) +{ + struct sk_buff *skb; + + if (iod->format == IPC_MULTI_RAW || iod->format == IPC_RAW) + skb = dev_alloc_skb(length); + else + skb = alloc_skb(length, GFP_ATOMIC); + + if (likely(skb)) { + skbpriv(skb)->iod = iod; + skbpriv(skb)->ld = ld; + } + return skb; +} + struct modemctl_ops { int (*modem_on) (struct modem_ctl *); int (*modem_off) (struct modem_ctl *); int (*modem_reset) (struct modem_ctl *); int (*modem_boot_on) (struct modem_ctl *); int (*modem_boot_off) (struct modem_ctl *); + int (*modem_boot_done) (struct modem_ctl *); int (*modem_force_crash_exit) (struct modem_ctl *); int (*modem_dump_reset) (struct modem_ctl *); }; -/* mif_common - common data for all io devices and link devices and a modem ctl - * commons : mc : iod : ld = 1 : 1 : M : N +/* for IPC Logger */ +struct mif_storage { + char *addr; + unsigned int cnt; +}; + +/* modem_shared - shared data for all io/link devices and a modem ctl + * msd : mc : iod : ld = 1 : 1 : M : N */ -struct mif_common { +struct modem_shared { /* list of link devices */ struct list_head link_dev_list; /* rb_tree root of io devices. */ struct rb_root iodevs_tree_chan; /* group by channel */ struct rb_root iodevs_tree_fmt; /* group by dev_format */ + + /* for IPC Logger */ + struct mif_storage storage; + spinlock_t lock; + + /* loopbacked IP address + * default is 0.0.0.0 (disabled) + * after you setted this, you can use IP packet loopback using this IP. + * exam: echo 1.2.3.4 > /sys/devices/virtual/misc/umts_multipdp/loopback + */ + __be32 loopback_ipaddr; }; struct modem_ctl { @@ -545,7 +580,7 @@ struct modem_ctl { char *name; struct modem_data *mdm_data; - struct mif_common commons; + struct modem_shared *msd; enum modem_state phone_state; struct sim_state sim_state; @@ -589,46 +624,146 @@ struct modem_ctl { bool usb_boot; #endif +#ifdef CONFIG_TDSCDMA_MODEM_SPRD8803 + unsigned gpio_ap_cp_int1; + unsigned gpio_ap_cp_int2; +#endif + +#ifdef CONFIG_SEC_DUAL_MODEM_MODE + unsigned gpio_sim_io_sel; + unsigned gpio_cp_ctrl1; + unsigned gpio_cp_ctrl2; +#endif + +#ifdef CONFIG_LINK_DEVICE_PLD + unsigned gpio_fpga_cs_n; +#endif + struct modemctl_ops ops; struct io_device *iod; struct io_device *bootd; + /* Wakelock for modem_ctl */ + struct wake_lock mc_wake_lock; + void (*gpio_revers_bias_clear)(void); void (*gpio_revers_bias_restore)(void); - /* TODO this will be move to struct mif_common */ - /* For debugging log */ - bool use_mif_log; - enum mif_event_id log_level; - atomic_t log_open; + bool need_switch_to_usb; + bool sim_polarity; +}; - struct workqueue_struct *evt_wq; - struct work_struct evt_work; - struct sk_buff_head evtq; +int sipc4_init_io_device(struct io_device *iod); +int sipc5_init_io_device(struct io_device *iod); - char log_path[MAX_MIF_LOG_PATH_LEN]; - struct file *log_fp; +/** + * sipc5_start_valid + * @cfg: configuration field of an SIPC5 link frame + * + * Returns TRUE if the start (configuration field) of an SIPC5 link frame + * is valid or returns FALSE if it is not valid. + * + */ +static inline int sipc5_start_valid(u8 cfg) +{ + return (cfg & SIPC5_START_MASK) == SIPC5_START_MASK; +} - bool fs_ready; - bool fs_failed; +/** + * sipc5_get_hdr_len + * @cfg: configuration field of an SIPC5 link frame + * + * Returns the length of SIPC5 link layer header in an SIPC5 link frame + * + */ +static inline unsigned sipc5_get_hdr_len(u8 cfg) +{ + if (cfg & SIPC5_EXT_FIELD_EXIST) { + if (cfg & SIPC5_CTL_FIELD_EXIST) + return SIPC5_HEADER_SIZE_WITH_CTL_FLD; + else + return SIPC5_HEADER_SIZE_WITH_EXT_LEN; + } else { + return SIPC5_MIN_HEADER_SIZE; + } +} - char *buff; -}; -#define to_modem_ctl(mif_common) \ - container_of(mif_common, struct modem_ctl, commons) +/** + * sipc5_get_ch_id + * @frm: pointer to an SIPC5 frame + * + * Returns the channel ID in an SIPC5 link frame + * + */ +static inline u8 sipc5_get_ch_id(u8 *frm) +{ + return *(frm + SIPC5_CH_ID_OFFSET); +} -int sipc4_init_io_device(struct io_device *iod); -int sipc5_init_io_device(struct io_device *iod); +/** + * sipc5_get_frame_sz16 + * @frm: pointer to an SIPC5 link frame + * + * Returns the length of an SIPC5 link frame without the extended length field + * + */ +static inline unsigned sipc5_get_frame_sz16(u8 *frm) +{ + return *((u16 *)(frm + SIPC5_LEN_OFFSET)); +} + +/** + * sipc5_get_frame_sz32 + * @frm: pointer to an SIPC5 frame + * + * Returns the length of an SIPC5 link frame with the extended length field + * + */ +static inline unsigned sipc5_get_frame_sz32(u8 *frm) +{ + return *((u32 *)(frm + SIPC5_LEN_OFFSET)); +} + +/** + * sipc5_calc_padding_size + * @len: length of an SIPC5 link frame + * + * Returns the padding size for an SIPC5 link frame + * + */ +static inline unsigned sipc5_calc_padding_size(unsigned len) +{ + unsigned residue = len & 0x3; + return residue ? (4 - residue) : 0; +} -int mif_init_log(struct modem_ctl *mc); -void mif_set_log_level(struct modem_ctl *mc); -int mif_open_log_file(struct modem_ctl *mc); -void mif_close_log_file(struct modem_ctl *mc); +extern void set_sromc_access(bool access); -void mif_irq_log(struct modem_ctl *mc, struct sk_buff *skb); -void mif_ipc_log(struct modem_ctl *mc, enum mif_event_id evt, - struct io_device *iod, struct link_device *ld, - u8 *data, unsigned size); -void mif_flush_logs(struct modem_ctl *mc); +#if defined(CONFIG_TDSCDMA_MODEM_SPRD8803) && defined(CONFIG_LINK_DEVICE_SPI) +extern int spi_sema_init(void); +extern int sprd_boot_done; +struct ipc_spi { + struct class *class; + struct device *dev; + struct cdev cdev; + dev_t devid; + + wait_queue_head_t waitq; + struct fasync_struct *async_queue; + u32 mailbox; + + unsigned long base; + unsigned long size; + void __iomem *mmio; + + int irq; + + struct completion comp; + atomic_t ref_sem; + unsigned long flags; + + const struct attribute_group *group; +}; +#endif #endif diff --git a/drivers/misc/modem_if/modem_sim_slot_switch.c b/drivers/misc/modem_if/modem_sim_slot_switch.c new file mode 100644 index 0000000..1dd4c67 --- /dev/null +++ b/drivers/misc/modem_if/modem_sim_slot_switch.c @@ -0,0 +1,92 @@ +#include <linux/i2c.h> +#include <linux/gpio.h> +#include <linux/delay.h> +#include <linux/err.h> + +#include <plat/gpio-cfg.h> + +#include <mach/gpio.h> + +extern struct class *sec_class; +struct device *slot_switch_dev; + +static ssize_t get_slot_switch(struct device *dev, struct device_attribute *attr, char *buf) +{ + int value; + + //return '0' slot path is '||', return '1' slot path is 'X' + value = gpio_get_value(GPIO_UIM_SIM_SEL); + printk("Current Slot is %x\n", value); + + return sprintf(buf, "%d\n", value); +} + +static ssize_t set_slot_switch(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) +{ + int value; + + sscanf(buf, "%d", &value); + + switch(value) { + case 0: + gpio_set_value(GPIO_UIM_SIM_SEL, 0); + printk("set slot switch to %x\n", gpio_get_value(GPIO_UIM_SIM_SEL)); + break; + case 1: + gpio_set_value(GPIO_UIM_SIM_SEL, 1); + printk("set slot switch to %x\n", gpio_get_value(GPIO_UIM_SIM_SEL)); + break; + default: + printk("Enter 0 or 1!!\n"); + } + + return size; +} + +static DEVICE_ATTR(slot_sel, S_IRUGO |S_IWUGO | S_IRUSR | S_IWUSR, get_slot_switch, set_slot_switch); + +static int __init slot_switch_manager_init(void) +{ + int ret = 0; + int err = 0; + + printk("slot_switch_manager_init\n"); + + //initailize uim_sim_switch gpio + err = gpio_request(GPIO_UIM_SIM_SEL, "PDA_ACTIVE"); + if (err) { + pr_err("fail to request gpio %s, gpio %d, errno %d\n", + "PDA_ACTIVE", GPIO_UIM_SIM_SEL, err); + } else { + gpio_direction_output(GPIO_UIM_SIM_SEL, 1); + s3c_gpio_setpull(GPIO_UIM_SIM_SEL, S3C_GPIO_PULL_NONE); +#if defined(CONFIG_MACH_T0_CHN_CTC) + gpio_set_value(GPIO_UIM_SIM_SEL, 1); +#else + gpio_set_value(GPIO_UIM_SIM_SEL, 0); +#endif + } + + //initailize slot switch device + slot_switch_dev = device_create(sec_class, + NULL, 0, NULL, "slot_switch"); + if (IS_ERR(slot_switch_dev)) + pr_err("Failed to create device(switch)!\n"); + + if (device_create_file(slot_switch_dev, &dev_attr_slot_sel) < 0) + pr_err("Failed to create device file(%s)!\n", + dev_attr_slot_sel.attr.name); + + return ret; +} + +static void __exit slot_switch_manager_exit(void) +{ +} + +module_init(slot_switch_manager_init); +module_exit(slot_switch_manager_exit); + +MODULE_AUTHOR("SAMSUNG ELECTRONICS CO., LTD"); +MODULE_DESCRIPTION("Slot Switch"); +MODULE_LICENSE("GPL"); diff --git a/drivers/misc/modem_if/modem_utils.c b/drivers/misc/modem_if/modem_utils.c index 24a9d19..d62aaa6 100644 --- a/drivers/misc/modem_if/modem_utils.c +++ b/drivers/misc/modem_if/modem_utils.c @@ -19,6 +19,8 @@ #include <linux/ip.h> #include <linux/tcp.h> #include <linux/udp.h> +#include <linux/rtc.h> +#include <linux/time.h> #include <net/ip.h> #include "modem_prj.h" @@ -27,6 +29,183 @@ #define CMD_SUSPEND ((unsigned short)(0x00CA)) #define CMD_RESUME ((unsigned short)(0x00CB)) +#define TX_SEPARATOR "mif: >>>>>>>>>> Outgoing packet " +#define RX_SEPARATOR "mif: Incoming packet <<<<<<<<<<" +#define LINE_SEPARATOR \ + "mif: ------------------------------------------------------------" +#define LINE_BUFF_SIZE 80 + +#ifdef CONFIG_LINK_DEVICE_DPRAM +#include "modem_link_device_dpram.h" +int mif_dump_dpram(struct io_device *iod) +{ + struct link_device *ld = get_current_link(iod); + struct dpram_link_device *dpld = to_dpram_link_device(ld); + u32 size = dpld->dp_size; + unsigned long read_len = 0; + struct sk_buff *skb; + char *buff; + + buff = kzalloc(size, GFP_ATOMIC); + if (!buff) { + pr_err("[MIF] <%s> alloc dpram buff failed\n", __func__); + return -ENOMEM; + } else { + dpld->dpram_dump(ld, buff); + } + + while (read_len < size) { + skb = alloc_skb(MAX_IPC_SKB_SIZE, GFP_ATOMIC); + if (!skb) { + pr_err("[MIF] <%s> alloc skb failed\n", __func__); + kfree(buff); + return -ENOMEM; + } + memcpy(skb_put(skb, MAX_IPC_SKB_SIZE), + buff + read_len, MAX_IPC_SKB_SIZE); + skb_queue_tail(&iod->sk_rx_q, skb); + read_len += MAX_IPC_SKB_SIZE; + wake_up(&iod->wq); + } + kfree(buff); + return 0; +} +#endif + +int mif_dump_log(struct modem_shared *msd, struct io_device *iod) +{ + struct sk_buff *skb; + unsigned long read_len = 0; + unsigned long int flags; + + spin_lock_irqsave(&msd->lock, flags); + while (read_len < MAX_MIF_BUFF_SIZE) { + skb = alloc_skb(MAX_IPC_SKB_SIZE, GFP_ATOMIC); + if (!skb) { + pr_err("[MIF] <%s> alloc skb failed\n", __func__); + spin_unlock_irqrestore(&msd->lock, flags); + return -ENOMEM; + } + memcpy(skb_put(skb, MAX_IPC_SKB_SIZE), + msd->storage.addr + read_len, MAX_IPC_SKB_SIZE); + skb_queue_tail(&iod->sk_rx_q, skb); + read_len += MAX_IPC_SKB_SIZE; + wake_up(&iod->wq); + } + spin_unlock_irqrestore(&msd->lock, flags); + return 0; +} + +static unsigned long long get_kernel_time(void) +{ + int this_cpu; + unsigned long flags; + unsigned long long time; + + preempt_disable(); + raw_local_irq_save(flags); + + this_cpu = smp_processor_id(); + time = cpu_clock(this_cpu); + + preempt_enable(); + raw_local_irq_restore(flags); + + return time; +} + +void mif_ipc_log(enum mif_log_id id, + struct modem_shared *msd, const char *data, size_t len) +{ + struct mif_ipc_block *block; + unsigned long int flags; + + spin_lock_irqsave(&msd->lock, flags); + + block = (struct mif_ipc_block *) + (msd->storage.addr + (MAX_LOG_SIZE * msd->storage.cnt)); + msd->storage.cnt = ((msd->storage.cnt + 1) < MAX_LOG_CNT) ? + msd->storage.cnt + 1 : 0; + + spin_unlock_irqrestore(&msd->lock, flags); + + block->id = id; + block->time = get_kernel_time(); + block->len = (len > MAX_IPC_LOG_SIZE) ? MAX_IPC_LOG_SIZE : len; + memcpy(block->buff, data, block->len); +} + +void _mif_irq_log(enum mif_log_id id, struct modem_shared *msd, + struct mif_irq_map map, const char *data, size_t len) +{ + struct mif_irq_block *block; + unsigned long int flags; + + spin_lock_irqsave(&msd->lock, flags); + + block = (struct mif_irq_block *) + (msd->storage.addr + (MAX_LOG_SIZE * msd->storage.cnt)); + msd->storage.cnt = ((msd->storage.cnt + 1) < MAX_LOG_CNT) ? + msd->storage.cnt + 1 : 0; + + spin_unlock_irqrestore(&msd->lock, flags); + + block->id = id; + block->time = get_kernel_time(); + memcpy(&(block->map), &map, sizeof(struct mif_irq_map)); + if (data) + memcpy(block->buff, data, + (len > MAX_IRQ_LOG_SIZE) ? MAX_IRQ_LOG_SIZE : len); +} + +void _mif_com_log(enum mif_log_id id, + struct modem_shared *msd, const char *format, ...) +{ + struct mif_common_block *block; + unsigned long int flags; + va_list args; + int ret; + + spin_lock_irqsave(&msd->lock, flags); + + block = (struct mif_common_block *) + (msd->storage.addr + (MAX_LOG_SIZE * msd->storage.cnt)); + msd->storage.cnt = ((msd->storage.cnt + 1) < MAX_LOG_CNT) ? + msd->storage.cnt + 1 : 0; + + spin_unlock_irqrestore(&msd->lock, flags); + + block->id = id; + block->time = get_kernel_time(); + + va_start(args, format); + ret = vsnprintf(block->buff, MAX_COM_LOG_SIZE, format, args); + va_end(args); +} + +void _mif_time_log(enum mif_log_id id, struct modem_shared *msd, + struct timespec epoch, const char *data, size_t len) +{ + struct mif_time_block *block; + unsigned long int flags; + + spin_lock_irqsave(&msd->lock, flags); + + block = (struct mif_time_block *) + (msd->storage.addr + (MAX_LOG_SIZE * msd->storage.cnt)); + msd->storage.cnt = ((msd->storage.cnt + 1) < MAX_LOG_CNT) ? + msd->storage.cnt + 1 : 0; + + spin_unlock_irqrestore(&msd->lock, flags); + + block->id = id; + block->time = get_kernel_time(); + memcpy(&block->epoch, &epoch, sizeof(struct timespec)); + + if (data) + memcpy(block->buff, data, + (len > MAX_IRQ_LOG_SIZE) ? MAX_IRQ_LOG_SIZE : len); +} /* dump2hex * dump data to hex as fast as possible. @@ -52,6 +231,23 @@ static inline int dump2hex(char *buf, const char *data, size_t len) return dest - buf; } +int pr_ipc(const char *str, const char *data, size_t len) +{ + struct timeval tv; + struct tm date; + unsigned char hexstr[128]; + + do_gettimeofday(&tv); + time_to_tm((tv.tv_sec - sys_tz.tz_minuteswest * 60), 0, &date); + + dump2hex(hexstr, data, (len > 40 ? 40 : len)); + + return pr_info("mif: %s: [%02d-%02d %02d:%02d:%02d.%03ld] %s\n", + str, date.tm_mon + 1, date.tm_mday, + date.tm_hour, date.tm_min, date.tm_sec, + (tv.tv_usec > 0 ? tv.tv_usec / 1000 : 0), hexstr); +} + /* print buffer as hex string */ int pr_buffer(const char *tag, const char *data, size_t data_len, size_t max_len) @@ -61,14 +257,14 @@ int pr_buffer(const char *tag, const char *data, size_t data_len, dump2hex(hexstr, data, len); /* don't change this printk to mif_debug for print this as level7 */ - return printk(KERN_DEBUG "%s(%u): %s%s\n", tag, data_len, hexstr, + return printk(KERN_INFO "mif: %s(%u): %s%s\n", tag, data_len, hexstr, len == data_len ? "" : " ..."); } -/* flow control CMfrom CP, it use in serial devices */ +/* flow control CM from CP, it use in serial devices */ int link_rx_flowctl_cmd(struct link_device *ld, const char *data, size_t len) { - struct mif_common *commons = &ld->mc->commons; + struct modem_shared *msd = ld->msd; unsigned short *cmd, *end = (unsigned short *)(data + len); mif_debug("flow control cmd: size=%d\n", len); @@ -76,13 +272,13 @@ int link_rx_flowctl_cmd(struct link_device *ld, const char *data, size_t len) for (cmd = (unsigned short *)data; cmd < end; cmd++) { switch (*cmd) { case CMD_SUSPEND: - iodevs_for_each(commons, iodev_netif_stop, 0); + iodevs_for_each(msd, iodev_netif_stop, 0); ld->raw_tx_suspended = true; mif_info("flowctl CMD_SUSPEND(%04X)\n", *cmd); break; case CMD_RESUME: - iodevs_for_each(commons, iodev_netif_wake, 0); + iodevs_for_each(msd, iodev_netif_wake, 0); ld->raw_tx_suspended = false; complete_all(&ld->raw_tx_resumed_by_cp); mif_info("flowctl CMD_RESUME(%04X)\n", *cmd); @@ -97,10 +293,10 @@ int link_rx_flowctl_cmd(struct link_device *ld, const char *data, size_t len) return 0; } -struct io_device *get_iod_with_channel(struct mif_common *commons, +struct io_device *get_iod_with_channel(struct modem_shared *msd, unsigned channel) { - struct rb_node *n = commons->iodevs_tree_chan.rb_node; + struct rb_node *n = msd->iodevs_tree_chan.rb_node; struct io_device *iodev; while (n) { iodev = rb_entry(n, struct io_device, node_chan); @@ -114,10 +310,10 @@ struct io_device *get_iod_with_channel(struct mif_common *commons, return NULL; } -struct io_device *get_iod_with_format(struct mif_common *commons, +struct io_device *get_iod_with_format(struct modem_shared *msd, enum dev_format format) { - struct rb_node *n = commons->iodevs_tree_fmt.rb_node; + struct rb_node *n = msd->iodevs_tree_fmt.rb_node; struct io_device *iodev; while (n) { iodev = rb_entry(n, struct io_device, node_fmt); @@ -131,10 +327,10 @@ struct io_device *get_iod_with_format(struct mif_common *commons, return NULL; } -struct io_device *insert_iod_with_channel(struct mif_common *commons, +struct io_device *insert_iod_with_channel(struct modem_shared *msd, unsigned channel, struct io_device *iod) { - struct rb_node **p = &commons->iodevs_tree_chan.rb_node; + struct rb_node **p = &msd->iodevs_tree_chan.rb_node; struct rb_node *parent = NULL; struct io_device *iodev; while (*p) { @@ -148,14 +344,14 @@ struct io_device *insert_iod_with_channel(struct mif_common *commons, return iodev; } rb_link_node(&iod->node_chan, parent, p); - rb_insert_color(&iod->node_chan, &commons->iodevs_tree_chan); + rb_insert_color(&iod->node_chan, &msd->iodevs_tree_chan); return NULL; } -struct io_device *insert_iod_with_format(struct mif_common *commons, +struct io_device *insert_iod_with_format(struct modem_shared *msd, enum dev_format format, struct io_device *iod) { - struct rb_node **p = &commons->iodevs_tree_fmt.rb_node; + struct rb_node **p = &msd->iodevs_tree_fmt.rb_node; struct rb_node *parent = NULL; struct io_device *iodev; while (*p) { @@ -169,14 +365,14 @@ struct io_device *insert_iod_with_format(struct mif_common *commons, return iodev; } rb_link_node(&iod->node_fmt, parent, p); - rb_insert_color(&iod->node_fmt, &commons->iodevs_tree_fmt); + rb_insert_color(&iod->node_fmt, &msd->iodevs_tree_fmt); return NULL; } -void iodevs_for_each(struct mif_common *commons, action_fn action, void *args) +void iodevs_for_each(struct modem_shared *msd, action_fn action, void *args) { struct io_device *iod; - struct rb_node *node = rb_first(&commons->iodevs_tree_chan); + struct rb_node *node = rb_first(&msd->iodevs_tree_chan); for (; node; node = rb_next(node)) { iod = rb_entry(node, struct io_device, node_chan); action(iod, args); @@ -202,22 +398,48 @@ void iodev_netif_stop(struct io_device *iod, void *args) static void iodev_set_tx_link(struct io_device *iod, void *args) { struct link_device *ld = (struct link_device *)args; - if (iod->io_typ == IODEV_NET && IS_CONNECTED(iod, ld)) { + if (iod->format == IPC_RAW && IS_CONNECTED(iod, ld)) { set_current_link(iod, ld); mif_err("%s -> %s\n", iod->name, ld->name); } } -void rawdevs_set_tx_link(struct mif_common *commons, enum modem_link link_type) +void rawdevs_set_tx_link(struct modem_shared *msd, enum modem_link link_type) { - struct link_device *ld = find_linkdev(commons, link_type); + struct link_device *ld = find_linkdev(msd, link_type); if (ld) - iodevs_for_each(commons, iodev_set_tx_link, ld); + iodevs_for_each(msd, iodev_set_tx_link, ld); +} + +/** + * ipv4str_to_be32 - ipv4 string to be32 (big endian 32bits integer) + * @return: return zero when errors occurred + */ +__be32 ipv4str_to_be32(const char *ipv4str, size_t count) +{ + unsigned char ip[4]; + char ipstr[16]; /* == strlen("xxx.xxx.xxx.xxx") + 1 */ + char *next = ipstr; + char *p; + int i; + + strncpy(ipstr, ipv4str, ARRAY_SIZE(ipstr)); + + for (i = 0; i < 4; i++) { + p = strsep(&next, "."); + if (kstrtou8(p, 10, &ip[i]) < 0) + return 0; /* == 0.0.0.0 */ + } + + return *((__be32 *)ip); } void mif_add_timer(struct timer_list *timer, unsigned long expire, void (*function)(unsigned long), unsigned long data) { + if (timer_pending(timer)) + return; + init_timer(timer); timer->expires = get_jiffies_64() + expire; timer->function = function; @@ -375,13 +597,12 @@ void print_sipc5_link_fmt_frame(const u8 *psrc) mif_err("-----------------------------------------------------------\n"); } -static void print_tcp_header(u8 *pkt) +static void strcat_tcp_header(char *buff, u8 *pkt) { - int i; - char tcp_flags[32]; struct tcphdr *tcph = (struct tcphdr *)pkt; - u8 *opt = pkt + TCP_HDR_SIZE; - unsigned opt_len = (tcph->doff << 2) - TCP_HDR_SIZE; + int eol; + char line[LINE_BUFF_SIZE]; + char flag_str[32]; /*------------------------------------------------------------------------- @@ -409,44 +630,54 @@ static void print_tcp_header(u8 *pkt) -------------------------------------------------------------------------*/ - memset(tcp_flags, 0, sizeof(tcp_flags)); + memset(line, 0, LINE_BUFF_SIZE); + snprintf(line, LINE_BUFF_SIZE, + "mif: TCP:: Src.Port %u, Dst.Port %u\n", + ntohs(tcph->source), ntohs(tcph->dest)); + strcat(buff, line); + + memset(line, 0, LINE_BUFF_SIZE); + snprintf(line, LINE_BUFF_SIZE, + "mif: TCP:: SEQ 0x%08X(%u), ACK 0x%08X(%u)\n", + ntohs(tcph->seq), ntohs(tcph->seq), + ntohs(tcph->ack_seq), ntohs(tcph->ack_seq)); + strcat(buff, line); + + memset(line, 0, LINE_BUFF_SIZE); + memset(flag_str, 0, sizeof(flag_str)); if (tcph->cwr) - strcat(tcp_flags, "CWR "); + strcat(flag_str, "CWR "); if (tcph->ece) - strcat(tcp_flags, "EC"); + strcat(flag_str, "ECE"); if (tcph->urg) - strcat(tcp_flags, "URG "); + strcat(flag_str, "URG "); if (tcph->ack) - strcat(tcp_flags, "ACK "); + strcat(flag_str, "ACK "); if (tcph->psh) - strcat(tcp_flags, "PSH "); + strcat(flag_str, "PSH "); if (tcph->rst) - strcat(tcp_flags, "RST "); + strcat(flag_str, "RST "); if (tcph->syn) - strcat(tcp_flags, "SYN "); + strcat(flag_str, "SYN "); if (tcph->fin) - strcat(tcp_flags, "FIN "); - - mif_err("TCP:: Src.Port %u, Dst.Port %u\n", - ntohs(tcph->source), ntohs(tcph->dest)); - mif_err("TCP:: SEQ 0x%08X(%u), ACK 0x%08X(%u)\n", - ntohs(tcph->seq), ntohs(tcph->seq), - ntohs(tcph->ack_seq), ntohs(tcph->ack_seq)); - mif_err("TCP:: Flags {%s}\n", tcp_flags); - mif_err("TCP:: Window %u, Checksum 0x%04X, Urg Pointer %u\n", + strcat(flag_str, "FIN "); + eol = strlen(flag_str) - 1; + if (eol > 0) + flag_str[eol] = 0; + snprintf(line, LINE_BUFF_SIZE, "mif: TCP:: Flags {%s}\n", flag_str); + strcat(buff, line); + + memset(line, 0, LINE_BUFF_SIZE); + snprintf(line, LINE_BUFF_SIZE, + "mif: TCP:: Window %u, Checksum 0x%04X, Urg Pointer %u\n", ntohs(tcph->window), ntohs(tcph->check), ntohs(tcph->urg_ptr)); - - if (opt_len > 0) { - mif_err("TCP:: Options {"); - for (i = 0; i < opt_len; i++) - mif_err("%02X ", opt[i]); - mif_err("}\n"); - } + strcat(buff, line); } -static void print_udp_header(u8 *pkt) +static void strcat_udp_header(char *buff, u8 *pkt) { struct udphdr *udph = (struct udphdr *)pkt; + char line[LINE_BUFF_SIZE]; /*------------------------------------------------------------------------- @@ -462,30 +693,43 @@ static void print_udp_header(u8 *pkt) -------------------------------------------------------------------------*/ - mif_err("UDP:: Src.Port %u, Dst.Port %u\n", + memset(line, 0, LINE_BUFF_SIZE); + snprintf(line, LINE_BUFF_SIZE, + "mif: UDP:: Src.Port %u, Dst.Port %u\n", ntohs(udph->source), ntohs(udph->dest)); - mif_err("UDP:: Length %u, Checksum 0x%04X\n", + strcat(buff, line); + + memset(line, 0, LINE_BUFF_SIZE); + snprintf(line, LINE_BUFF_SIZE, + "mif: UDP:: Length %u, Checksum 0x%04X\n", ntohs(udph->len), ntohs(udph->check)); + strcat(buff, line); - if (ntohs(udph->dest) == 53) - mif_err("UDP:: DNS query!!!\n"); + if (ntohs(udph->dest) == 53) { + memset(line, 0, LINE_BUFF_SIZE); + snprintf(line, LINE_BUFF_SIZE, "mif: UDP:: DNS query!!!\n"); + strcat(buff, line); + } - if (ntohs(udph->source) == 53) - mif_err("UDP:: DNS response!!!\n"); + if (ntohs(udph->source) == 53) { + memset(line, 0, LINE_BUFF_SIZE); + snprintf(line, LINE_BUFF_SIZE, "mif: UDP:: DNS response!!!\n"); + strcat(buff, line); + } } -void print_ip4_packet(u8 *ip_pkt) +void print_ip4_packet(u8 *ip_pkt, bool tx) { - char ip_flags[16]; + char *buff; struct iphdr *iph = (struct iphdr *)ip_pkt; u8 *pkt = ip_pkt + (iph->ihl << 2); u16 flags = (ntohs(iph->frag_off) & 0xE000); u16 frag_off = (ntohs(iph->frag_off) & 0x1FFF); - - mif_err("-----------------------------------------------------------\n"); + int eol; + char line[LINE_BUFF_SIZE]; + char flag_str[16]; /*--------------------------------------------------------------------------- - IPv4 Header Format +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ @@ -513,40 +757,234 @@ void print_ip4_packet(u8 *ip_pkt) ---------------------------------------------------------------------------*/ - memset(ip_flags, 0, sizeof(ip_flags)); - if (flags & IP_CE) - strcat(ip_flags, "C"); - if (flags & IP_DF) - strcat(ip_flags, "D"); - if (flags & IP_MF) - strcat(ip_flags, "M"); + if (iph->version != 4) + return; - mif_err("IP4:: Version %u, Header Length %u, TOS %u, Length %u\n", + buff = kzalloc(4096, GFP_ATOMIC); + if (!buff) + return; + + + memset(line, 0, LINE_BUFF_SIZE); + if (tx) + snprintf(line, LINE_BUFF_SIZE, "\n%s\n", TX_SEPARATOR); + else + snprintf(line, LINE_BUFF_SIZE, "\n%s\n", RX_SEPARATOR); + strcat(buff, line); + + memset(line, 0, LINE_BUFF_SIZE); + snprintf(line, LINE_BUFF_SIZE, "%s\n", LINE_SEPARATOR); + strcat(buff, line); + + memset(line, 0, LINE_BUFF_SIZE); + snprintf(line, LINE_BUFF_SIZE, + "mif: IP4:: Version %u, Header Length %u, TOS %u, Length %u\n", iph->version, (iph->ihl << 2), iph->tos, ntohs(iph->tot_len)); - mif_err("IP4:: I%u, Fragment Offset %u\n", + strcat(buff, line); + + memset(line, 0, LINE_BUFF_SIZE); + snprintf(line, LINE_BUFF_SIZE, + "mif: IP4:: ID %u, Fragment Offset %u\n", ntohs(iph->id), frag_off); - mif_err("IP4:: Flags {%s}\n", ip_flags); - mif_err("IP4:: TTL %u, Protocol %u, Header Checksum 0x%04X\n", + strcat(buff, line); + + memset(line, 0, LINE_BUFF_SIZE); + memset(flag_str, 0, sizeof(flag_str)); + if (flags & IP_CE) + strcat(flag_str, "CE "); + if (flags & IP_DF) + strcat(flag_str, "DF "); + if (flags & IP_MF) + strcat(flag_str, "MF "); + eol = strlen(flag_str) - 1; + if (eol > 0) + flag_str[eol] = 0; + snprintf(line, LINE_BUFF_SIZE, "mif: IP4:: Flags {%s}\n", flag_str); + strcat(buff, line); + + memset(line, 0, LINE_BUFF_SIZE); + snprintf(line, LINE_BUFF_SIZE, + "mif: IP4:: TTL %u, Protocol %u, Header Checksum 0x%04X\n", iph->ttl, iph->protocol, ntohs(iph->check)); - mif_err("IP4:: Src.IP %u.%u.%u.%u, Dst.IP %u.%u.%u.%u\n", + strcat(buff, line); + + memset(line, 0, LINE_BUFF_SIZE); + snprintf(line, LINE_BUFF_SIZE, + "mif: IP4:: Src.IP %u.%u.%u.%u, Dst.IP %u.%u.%u.%u\n", ip_pkt[12], ip_pkt[13], ip_pkt[14], ip_pkt[15], ip_pkt[16], ip_pkt[17], ip_pkt[18], ip_pkt[19]); + strcat(buff, line); switch (iph->protocol) { - case 6: - /* TCP */ - print_tcp_header(pkt); + case 6: /* TCP */ + strcat_tcp_header(buff, pkt); break; - case 17: - /* UDP */ - print_udp_header(pkt); + case 17: /* UDP */ + strcat_udp_header(buff, pkt); break; default: break; } - mif_err("-----------------------------------------------------------\n"); + memset(line, 0, LINE_BUFF_SIZE); + snprintf(line, LINE_BUFF_SIZE, "%s\n", LINE_SEPARATOR); + strcat(buff, line); + + pr_info("%s", buff); + + kfree(buff); +} + +bool is_dns_packet(u8 *ip_pkt) +{ + struct iphdr *iph = (struct iphdr *)ip_pkt; + struct udphdr *udph = (struct udphdr *)(ip_pkt + (iph->ihl << 2)); + + /* If this packet is not a UDP packet, return here. */ + if (iph->protocol != 17) + return false; + + if (ntohs(udph->dest) == 53 || ntohs(udph->source) == 53) + return true; + else + return false; +} + +bool is_syn_packet(u8 *ip_pkt) +{ + struct iphdr *iph = (struct iphdr *)ip_pkt; + struct tcphdr *tcph = (struct tcphdr *)(ip_pkt + (iph->ihl << 2)); + + /* If this packet is not a TCP packet, return here. */ + if (iph->protocol != 6) + return false; + + if (tcph->syn || tcph->fin) + return true; + else + return false; +} + +int memcmp16_to_io(const void __iomem *to, void *from, int size) +{ + u16 *d = (u16 *)to; + u16 *s = (u16 *)from; + int count = size >> 1; + int diff = 0; + int i; + u16 d1; + u16 s1; + + for (i = 0; i < count; i++) { + d1 = ioread16(d); + s1 = *s; + if (d1 != s1) { + diff++; + mif_err("ERR! [%d] d:0x%04X != s:0x%04X\n", i, d1, s1); + } + d++; + s++; + } + + return diff; +} + +int mif_test_dpram(char *dp_name, u8 __iomem *start, u32 size) +{ + u8 __iomem *dst; + int i; + u16 val; + + mif_info("%s: start = 0x%p, size = %d\n", dp_name, start, size); + + dst = start; + for (i = 0; i < (size >> 1); i++) { + iowrite16((i & 0xFFFF), dst); + dst += 2; + } + + dst = start; + for (i = 0; i < (size >> 1); i++) { + val = ioread16(dst); + if (val != (i & 0xFFFF)) { + mif_info("%s: ERR! dst[%d] 0x%04X != 0x%04X\n", + dp_name, i, val, (i & 0xFFFF)); + return -EINVAL; + } + dst += 2; + } + + dst = start; + for (i = 0; i < (size >> 1); i++) { + iowrite16(0x00FF, dst); + dst += 2; + } + + dst = start; + for (i = 0; i < (size >> 1); i++) { + val = ioread16(dst); + if (val != 0x00FF) { + mif_info("%s: ERR! dst[%d] 0x%04X != 0x00FF\n", + dp_name, i, val); + return -EINVAL; + } + dst += 2; + } + + dst = start; + for (i = 0; i < (size >> 1); i++) { + iowrite16(0x0FF0, dst); + dst += 2; + } + + dst = start; + for (i = 0; i < (size >> 1); i++) { + val = ioread16(dst); + if (val != 0x0FF0) { + mif_info("%s: ERR! dst[%d] 0x%04X != 0x0FF0\n", + dp_name, i, val); + return -EINVAL; + } + dst += 2; + } + + dst = start; + for (i = 0; i < (size >> 1); i++) { + iowrite16(0xFF00, dst); + dst += 2; + } + + dst = start; + for (i = 0; i < (size >> 1); i++) { + val = ioread16(dst); + if (val != 0xFF00) { + mif_info("%s: ERR! dst[%d] 0x%04X != 0xFF00\n", + dp_name, i, val); + return -EINVAL; + } + dst += 2; + } + + dst = start; + for (i = 0; i < (size >> 1); i++) { + iowrite16(0, dst); + dst += 2; + } + + dst = start; + for (i = 0; i < (size >> 1); i++) { + val = ioread16(dst); + if (val != 0) { + mif_info("%s: ERR! dst[%d] 0x%04X != 0\n", + dp_name, i, val); + return -EINVAL; + } + dst += 2; + } + + mif_info("%s: PASS!!!\n", dp_name); + return 0; } diff --git a/drivers/misc/modem_if/modem_utils.h b/drivers/misc/modem_if/modem_utils.h index 60e4820..7225ec2 100644 --- a/drivers/misc/modem_if/modem_utils.h +++ b/drivers/misc/modem_if/modem_utils.h @@ -19,14 +19,110 @@ #define IS_CONNECTED(iod, ld) ((iod)->link_types & LINKTYPE((ld)->link_type)) +#define MAX_MIF_BUFF_SIZE 0x80000 /* 512kb */ +#define MAX_MIF_SEPA_SIZE 32 +#define MIF_SEPARATOR "IPC_LOGGER(VER1.1)" +#define MIF_SEPARATOR_DPRAM "DPRAM_LOGGER(VER1.1)" +#define MAX_IPC_SKB_SIZE 4096 +#define MAX_LOG_SIZE 64 + +#define MAX_LOG_CNT (MAX_MIF_BUFF_SIZE / MAX_LOG_SIZE) +#define MIF_ID_SIZE sizeof(enum mif_log_id) + +#define MAX_IPC_LOG_SIZE \ + (MAX_LOG_SIZE - sizeof(enum mif_log_id) \ + - sizeof(unsigned long long) - sizeof(size_t)) +#define MAX_IRQ_LOG_SIZE \ + (MAX_LOG_SIZE - sizeof(enum mif_log_id) \ + - sizeof(unsigned long long) - sizeof(struct mif_irq_map)) +#define MAX_COM_LOG_SIZE \ + (MAX_LOG_SIZE - sizeof(enum mif_log_id) \ + - sizeof(unsigned long long)) +#define MAX_TIM_LOG_SIZE \ + (MAX_LOG_SIZE - sizeof(enum mif_log_id) \ + - sizeof(unsigned long long) - sizeof(struct timespec)) + +enum mif_log_id { + MIF_IPC_RL2AP = 1, + MIF_IPC_AP2CP, + MIF_IPC_CP2AP, + MIF_IPC_AP2RL, + MIF_IRQ, + MIF_COM, + MIF_TIME +}; + +struct mif_irq_map { + u16 magic; + u16 access; + + u16 fmt_tx_in; + u16 fmt_tx_out; + u16 fmt_rx_in; + u16 fmt_rx_out; + + u16 raw_tx_in; + u16 raw_tx_out; + u16 raw_rx_in; + u16 raw_rx_out; + + u16 cp2ap; +}; + +struct mif_ipc_block { + enum mif_log_id id; + unsigned long long time; + size_t len; + char buff[MAX_IPC_LOG_SIZE]; +}; + +struct mif_irq_block { + enum mif_log_id id; + unsigned long long time; + struct mif_irq_map map; + char buff[MAX_IRQ_LOG_SIZE]; +}; + +struct mif_common_block { + enum mif_log_id id; + unsigned long long time; + char buff[MAX_COM_LOG_SIZE]; +}; + +struct mif_time_block { + enum mif_log_id id; + unsigned long long time; + struct timespec epoch; + char buff[MAX_TIM_LOG_SIZE]; +}; + +int mif_dump_dpram(struct io_device *); +int mif_dump_log(struct modem_shared *, struct io_device *); + +#define mif_irq_log(msd, map, data, len) \ + _mif_irq_log(MIF_IRQ, msd, map, data, len) +#define mif_com_log(msd, format, ...) \ + _mif_com_log(MIF_COM, msd, pr_fmt(format), ##__VA_ARGS__) +#define mif_time_log(msd, epoch, data, len) \ + _mif_time_log(MIF_TIME, msd, epoch, data, len) + +void mif_ipc_log(enum mif_log_id, + struct modem_shared *, const char *, size_t); +void _mif_irq_log(enum mif_log_id, + struct modem_shared *, struct mif_irq_map, const char *, size_t); +void _mif_com_log(enum mif_log_id, + struct modem_shared *, const char *, ...); +void _mif_time_log(enum mif_log_id, + struct modem_shared *, struct timespec, const char *, size_t); + /** find_linkdev - find a link device - * @commons: struct mif_common + * @msd: struct modem_shared * */ -static inline struct link_device *find_linkdev(struct mif_common *commons, +static inline struct link_device *find_linkdev(struct modem_shared *msd, enum modem_link link_type) { struct link_device *ld; - list_for_each_entry(ld, &commons->link_dev_list, list) { + list_for_each_entry(ld, &msd->link_dev_list, list) { if (ld->link_type == link_type) return ld; } @@ -44,6 +140,9 @@ static inline unsigned int countbits(unsigned int n) return i; } +/* print IPC message as hex string with UTC time */ +int pr_ipc(const char *str, const char *data, size_t len); + /* print buffer as hex string */ int pr_buffer(const char *tag, const char *data, size_t data_len, size_t max_len); @@ -63,41 +162,43 @@ int link_rx_flowctl_cmd(struct link_device *ld, const char *data, size_t len); /* get iod from tree functions */ -struct io_device *get_iod_with_format(struct mif_common *commons, +struct io_device *get_iod_with_format(struct modem_shared *msd, enum dev_format format); -struct io_device *get_iod_with_channel(struct mif_common *commons, +struct io_device *get_iod_with_channel(struct modem_shared *msd, unsigned channel); static inline struct io_device *link_get_iod_with_format( struct link_device *ld, enum dev_format format) { - struct io_device *iod = get_iod_with_format(&ld->mc->commons, format); + struct io_device *iod = get_iod_with_format(ld->msd, format); return (iod && IS_CONNECTED(iod, ld)) ? iod : NULL; } static inline struct io_device *link_get_iod_with_channel( struct link_device *ld, unsigned channel) { - struct io_device *iod = get_iod_with_channel(&ld->mc->commons, channel); + struct io_device *iod = get_iod_with_channel(ld->msd, channel); return (iod && IS_CONNECTED(iod, ld)) ? iod : NULL; } /* insert iod to tree functions */ -struct io_device *insert_iod_with_format(struct mif_common *commons, +struct io_device *insert_iod_with_format(struct modem_shared *msd, enum dev_format format, struct io_device *iod); -struct io_device *insert_iod_with_channel(struct mif_common *commons, +struct io_device *insert_iod_with_channel(struct modem_shared *msd, unsigned channel, struct io_device *iod); /* iodev for each */ typedef void (*action_fn)(struct io_device *iod, void *args); -void iodevs_for_each(struct mif_common *commons, action_fn action, void *args); +void iodevs_for_each(struct modem_shared *msd, action_fn action, void *args); /* netif wake/stop queue of iod */ void iodev_netif_wake(struct io_device *iod, void *args); void iodev_netif_stop(struct io_device *iod, void *args); /* change tx_link of raw devices */ -void rawdevs_set_tx_link(struct mif_common *commons, enum modem_link link_type); +void rawdevs_set_tx_link(struct modem_shared *msd, enum modem_link link_type); + +__be32 ipv4str_to_be32(const char *ipv4str, size_t count); void mif_add_timer(struct timer_list *timer, unsigned long expire, void (*function)(unsigned long), unsigned long data); @@ -181,6 +282,23 @@ void print_sipc5_link_fmt_frame(const u8 *psrc); -------------------------------------------------------------------------*/ #define UDP_HDR_SIZE 8 -void print_ip4_packet(u8 *ip_pkt); +void print_ip4_packet(u8 *ip_pkt, bool tx); +bool is_dns_packet(u8 *ip_pkt); +bool is_syn_packet(u8 *ip_pkt); + +int memcmp16_to_io(const void __iomem *to, void *from, int size); +int mif_test_dpram(char *dp_name, u8 __iomem *start, u32 size); + +static const inline char *get_dev_name(int dev) +{ + if (dev == IPC_FMT) + return "FMT"; + else if (dev == IPC_RAW) + return "RAW"; + else if (dev == IPC_RFS) + return "RFS"; + else + return "NONE"; +} #endif/*__MODEM_UTILS_H__*/ diff --git a/drivers/misc/modem_if/modem_variation.h b/drivers/misc/modem_if/modem_variation.h index 596abd1..b5ec61b 100644 --- a/drivers/misc/modem_if/modem_variation.h +++ b/drivers/misc/modem_if/modem_variation.h @@ -73,6 +73,18 @@ DECLARE_MODEM_INIT(mdm6600); DECLARE_MODEM_INIT_DUMMY(mdm6600) #endif +#ifdef CONFIG_GSM_MODEM_ESC6270 +DECLARE_MODEM_INIT(esc6270); +#else +DECLARE_MODEM_INIT_DUMMY(esc6270) +#endif + +#ifdef CONFIG_TDSCDMA_MODEM_SPRD8803 +DECLARE_MODEM_INIT(sprd8803); +#else +DECLARE_MODEM_INIT_DUMMY(sprd8803) +#endif + /* link device support */ DECLARE_LINK_INIT_DUMMY(undefined) @@ -88,6 +100,12 @@ DECLARE_LINK_INIT(dpram); DECLARE_LINK_INIT_DUMMY(dpram) #endif +#ifdef CONFIG_LINK_DEVICE_PLD +DECLARE_LINK_INIT(pld); +#else +DECLARE_LINK_INIT_DUMMY(pld) +#endif + #ifdef CONFIG_LINK_DEVICE_SPI DECLARE_LINK_INIT(spi); #else @@ -120,6 +138,8 @@ static modem_init_call modem_init_func[] = { MODEM_INIT_CALL(cbp72), MODEM_INIT_CALL(cmc221), MODEM_INIT_CALL(mdm6600), + MODEM_INIT_CALL(esc6270), + MODEM_INIT_CALL(sprd8803), MODEM_INIT_CALL(dummy), }; @@ -132,6 +152,7 @@ static link_init_call link_init_func[] = { LINK_INIT_CALL(usb), LINK_INIT_CALL(hsic), LINK_INIT_CALL(c2c), + LINK_INIT_CALL(pld), }; static int call_modem_init_func(struct modem_ctl *mc, struct modem_data *pdata) diff --git a/drivers/misc/modem_if/sipc4_io_device.c b/drivers/misc/modem_if/sipc4_io_device.c index 94cd85b..28f95f7 100644 --- a/drivers/misc/modem_if/sipc4_io_device.c +++ b/drivers/misc/modem_if/sipc4_io_device.c @@ -85,6 +85,35 @@ static ssize_t store_waketime(struct device *dev, static struct device_attribute attr_waketime = __ATTR(waketime, S_IRUGO | S_IWUSR, show_waketime, store_waketime); +static ssize_t show_loopback(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct miscdevice *miscdev = dev_get_drvdata(dev); + struct modem_shared *msd = + container_of(miscdev, struct io_device, miscdev)->msd; + unsigned char *ip = (unsigned char *)&msd->loopback_ipaddr; + char *p = buf; + + p += sprintf(buf, "%u.%u.%u.%u\n", ip[0], ip[1], ip[2], ip[3]); + + return p - buf; +} + +static ssize_t store_loopback(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct miscdevice *miscdev = dev_get_drvdata(dev); + struct modem_shared *msd = + container_of(miscdev, struct io_device, miscdev)->msd; + + msd->loopback_ipaddr = ipv4str_to_be32(buf, count); + + return count; +} + +static struct device_attribute attr_loopback = + __ATTR(loopback, S_IRUGO | S_IWUSR, show_loopback, store_loopback); + static int get_header_size(struct io_device *iod) { switch (iod->format) { @@ -300,7 +329,7 @@ static int rx_hdlc_data_check(struct io_device *iod, struct link_device *ld, * make skb for header info first */ if (iod->format == IPC_RFS && !hdr->frag_len) { - skb = rx_alloc_skb(head_size, GFP_ATOMIC, iod, ld); + skb = rx_alloc_skb(head_size, iod, ld); if (unlikely(!skb)) return -ENOMEM; memcpy(skb_put(skb, head_size), hdr->hdr, head_size); @@ -311,17 +340,11 @@ static int rx_hdlc_data_check(struct io_device *iod, struct link_device *ld, * MAX_RXDATA_SIZE, this packet will split to * multiple packets */ - if (iod->use_handover) - alloc_size += sizeof(struct ethhdr); - - skb = rx_alloc_skb(alloc_size, GFP_ATOMIC, iod, ld); + skb = rx_alloc_skb(alloc_size, iod, ld); if (unlikely(!skb)) { fragdata(iod, ld)->realloc_offset = continue_len; return -ENOMEM; } - - if (iod->use_handover) - skb_reserve(skb, sizeof(struct ethhdr)); fragdata(iod, ld)->skb_recv = skb; } @@ -357,7 +380,7 @@ static int rx_hdlc_data_check(struct io_device *iod, struct link_device *ld, alloc_size = min(rest_len, MAX_RXDATA_SIZE); - skb = rx_alloc_skb(alloc_size, GFP_ATOMIC, iod, ld); + skb = rx_alloc_skb(alloc_size, iod, ld); if (unlikely(!skb)) { fragdata(iod, ld)->realloc_offset = done_len; return -ENOMEM; @@ -399,8 +422,7 @@ static int rx_multi_fmt_frame(struct sk_buff *rx_skb) return 0; } else { struct fmt_hdr *fh = NULL; - skb = rx_alloc_skb(MAX_MULTI_FMT_SIZE, GFP_ATOMIC, - iod, ld); + skb = rx_alloc_skb(MAX_MULTI_FMT_SIZE, iod, ld); if (!skb) { mif_err("<%d> alloc_skb fail\n", __LINE__); @@ -482,8 +504,7 @@ static int rx_multi_fmt_frame_sipc42(struct sk_buff *rx_skb) return 0; } else { struct fmt_hdr *fh = NULL; - skb = rx_alloc_skb(MAX_MULTI_FMT_SIZE, GFP_ATOMIC, - real_iod, ld); + skb = rx_alloc_skb(MAX_MULTI_FMT_SIZE, real_iod, ld); if (!skb) { mif_err("alloc_skb fail\n"); return -ENOMEM; @@ -631,6 +652,9 @@ static int rx_multipdp(struct sk_buff *skb) struct io_device *real_iod = NULL; ch = raw_header->channel; + if (ch == DATA_LOOPBACK_CHANNEL && ld->msd->loopback_ipaddr) + ch = RMNET0_CH_ID; + real_iod = link_get_iod_with_channel(ld, 0x20 | ch); if (!real_iod) { mif_err("wrong channel %d\n", ch); @@ -828,7 +852,7 @@ static int rx_rfs_packet(struct io_device *iod, struct link_device *ld, } } - skb = rx_alloc_skb(size, GFP_ATOMIC, iod, ld); + skb = rx_alloc_skb(size, iod, ld); if (unlikely(!skb)) { mif_err("alloc_skb fail\n"); return -ENOMEM; @@ -850,6 +874,9 @@ static int io_dev_recv_data_from_link_dev(struct io_device *iod, { struct sk_buff *skb; int err; + unsigned int alloc_size, rest_len; + char *cur; + /* check the iod(except IODEV_DUMMY) is open? * if the iod is MULTIPDP, check this data on rx_iodev_skb_raw() @@ -887,18 +914,39 @@ static int io_dev_recv_data_from_link_dev(struct io_device *iod, case IPC_BOOT: case IPC_RAMDUMP: /* save packet to sk_buff */ - skb = rx_alloc_skb(len, GFP_ATOMIC, iod, ld); - if (!skb) { - mif_err("fail alloc skb (%d)\n", __LINE__); - return -ENOMEM; - } + skb = rx_alloc_skb(len, iod, ld); + if (skb) { + mif_debug("boot len : %d\n", len); - mif_debug("boot len : %d\n", len); + memcpy(skb_put(skb, len), data, len); + skb_queue_tail(&iod->sk_rx_q, skb); + mif_debug("skb len : %d\n", skb->len); - memcpy(skb_put(skb, len), data, len); - skb_queue_tail(&iod->sk_rx_q, skb); - mif_debug("skb len : %d\n", skb->len); + wake_up(&iod->wq); + return len; + } + /* 32KB page alloc fail case, alloc 3.5K a page.. */ + mif_info("(%d)page fail, alloc fragment pages\n", len); + + rest_len = len; + cur = (char *)data; + while (rest_len) { + alloc_size = min_t(unsigned int, MAX_RXDATA_SIZE, + rest_len); + skb = rx_alloc_skb(alloc_size, iod, ld); + if (!skb) { + mif_err("fail alloc skb (%d)\n", __LINE__); + return -ENOMEM; + } + mif_debug("boot len : %d\n", alloc_size); + memcpy(skb_put(skb, alloc_size), cur, alloc_size); + skb_queue_tail(&iod->sk_rx_q, skb); + mif_debug("skb len : %d\n", skb->len); + + rest_len -= alloc_size; + cur += alloc_size; + } wake_up(&iod->wq); return len; @@ -950,7 +998,7 @@ static void io_dev_sim_state_changed(struct io_device *iod, bool sim_online) static int misc_open(struct inode *inode, struct file *filp) { struct io_device *iod = to_io_device(filp->private_data); - struct mif_common *commons = &iod->mc->commons; + struct modem_shared *msd = iod->msd; struct link_device *ld; int ret; filp->private_data = (void *)iod; @@ -958,7 +1006,7 @@ static int misc_open(struct inode *inode, struct file *filp) mif_err("iod = %s\n", iod->name); atomic_inc(&iod->opened); - list_for_each_entry(ld, &commons->link_dev_list, list) { + list_for_each_entry(ld, &msd->link_dev_list, list) { if (IS_CONNECTED(iod, ld) && ld->init_comm) { ret = ld->init_comm(ld, iod); if (ret < 0) { @@ -975,14 +1023,14 @@ static int misc_open(struct inode *inode, struct file *filp) static int misc_release(struct inode *inode, struct file *filp) { struct io_device *iod = (struct io_device *)filp->private_data; - struct mif_common *commons = &iod->mc->commons; + struct modem_shared *msd = iod->msd; struct link_device *ld; mif_err("iod = %s\n", iod->name); atomic_dec(&iod->opened); skb_queue_purge(&iod->sk_rx_q); - list_for_each_entry(ld, &commons->link_dev_list, list) { + list_for_each_entry(ld, &msd->link_dev_list, list) { if (IS_CONNECTED(iod, ld) && ld->terminate_comm) ld->terminate_comm(ld, iod); } @@ -1002,6 +1050,9 @@ static unsigned int misc_poll(struct file *filp, struct poll_table_struct *wait) } else if ((iod->mc->phone_state == STATE_CRASH_RESET) || (iod->mc->phone_state == STATE_CRASH_EXIT) || (iod->mc->phone_state == STATE_NV_REBUILDING) || +#if defined(CONFIG_SEC_DUAL_MODEM_MODE) + (iod->mc->phone_state == STATE_MODEM_SWITCH) || +#endif (iod->mc->sim_state.changed)) { if (iod->format == IPC_RAW) { msleep(20); @@ -1019,6 +1070,9 @@ static long misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) struct io_device *iod = (struct io_device *)filp->private_data; struct link_device *ld = get_current_link(iod); char cpinfo_buf[530] = "CP Crash "; + unsigned long size; + int ret; + char str[TASK_COMM_LEN]; mif_debug("cmd = 0x%x\n", cmd); @@ -1044,8 +1098,8 @@ static long misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) return iod->mc->ops.modem_boot_off(iod->mc); /* TODO - will remove this command after ril updated */ - case IOCTL_MODEM_START: - mif_debug("misc_ioctl : IOCTL_MODEM_START\n"); + case IOCTL_MODEM_BOOT_DONE: + mif_debug("misc_ioctl : IOCTL_MODEM_BOOT_DONE\n"); return 0; case IOCTL_MODEM_STATUS: @@ -1056,10 +1110,13 @@ static long misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) (p_state == STATE_CRASH_EXIT)) { mif_err("<%s> send err state : %d\n", iod->name, p_state); - } else if (iod->mc->sim_state.changed) { + } else if (iod->mc->sim_state.changed && + !strcmp(get_task_comm(str, get_current()), "rild")) { int s_state = iod->mc->sim_state.online ? STATE_SIM_ATTACH : STATE_SIM_DETACH; iod->mc->sim_state.changed = false; + + mif_info("SIM states (%d) to %s\n", s_state, str); return s_state; } else if (p_state == STATE_NV_REBUILDING) { mif_info("send nv rebuild state : %d\n", @@ -1074,7 +1131,7 @@ static long misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) if (iod->format != IPC_MULTI_RAW) return -EINVAL; - iodevs_for_each(&iod->mc->commons, iodev_netif_stop, 0); + iodevs_for_each(iod->msd, iodev_netif_stop, 0); return 0; case IOCTL_MODEM_PROTOCOL_RESUME: @@ -1083,7 +1140,7 @@ static long misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) if (iod->format != IPC_MULTI_RAW) return -EINVAL; - iodevs_for_each(&iod->mc->commons, iodev_netif_wake, 0); + iodevs_for_each(iod->msd, iodev_netif_wake, 0); return 0; case IOCTL_MODEM_DUMP_START: @@ -1113,6 +1170,38 @@ static long misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) mif_err("misc_ioctl : IOCTL_MODEM_DUMP_RESET\n"); return iod->mc->ops.modem_dump_reset(iod->mc); +#if defined(CONFIG_SEC_DUAL_MODEM_MODE) + case IOCTL_MODEM_SWITCH_MODEM: + mif_err("misc_ioctl : IOCTL_MODEM_SWITCH_MODEM\n"); + iod->mc->phone_state = STATE_MODEM_SWITCH; + wake_up(&iod->wq); + return 0; +#endif + + case IOCTL_MIF_LOG_DUMP: + size = MAX_MIF_BUFF_SIZE; + ret = copy_to_user((void __user *)arg, &size, + sizeof(unsigned long)); + if (ret < 0) + return -EFAULT; + + mif_dump_log(iod->mc->msd, iod); + return 0; + + case IOCTL_MIF_DPRAM_DUMP: +#ifdef CONFIG_LINK_DEVICE_DPRAM + if (iod->mc->mdm_data->link_types & LINKTYPE(LINKDEV_DPRAM)) { + size = iod->mc->mdm_data->dpram_ctl->dp_size; + ret = copy_to_user((void __user *)arg, &size, + sizeof(unsigned long)); + if (ret < 0) + return -EFAULT; + mif_dump_dpram(iod); + return 0; + } +#endif + return -EINVAL; + default: /* If you need to handle the ioctl for specific link device, * then assign the link ioctl handler to ld->ioctl @@ -1246,6 +1335,8 @@ static ssize_t misc_read(struct file *filp, char *buf, size_t count, struct io_device *iod = (struct io_device *)filp->private_data; struct sk_buff *skb = NULL; int pktsize = 0; + unsigned int rest_len, copy_len; + char *cur = buf; skb = skb_dequeue(&iod->sk_rx_q); if (!skb) { @@ -1254,43 +1345,67 @@ static ssize_t misc_read(struct file *filp, char *buf, size_t count, } mif_debug("<%s> skb->len : %d\n", iod->name, skb->len); - if (skb->len > count) { - /* BOOT device receviced rx data as serial stream, return data - by User requested size */ - if (iod->format == IPC_BOOT) { - mif_err("skb->len %d > count %d\n", skb->len, - count); - pr_skb("BOOT-wRX", skb); - if (copy_to_user(buf, skb->data, count) != 0) { + if (iod->format == IPC_BOOT) { + pktsize = rest_len = count; + while (rest_len) { + if (skb->len > rest_len) { + /* BOOT device receviced rx data as serial + stream, return data by User requested size */ + mif_err("skb->len %d > count %d\n", skb->len, + rest_len); + pr_skb("BOOT-wRX", skb); + if (copy_to_user(cur, skb->data, rest_len) + != 0) { + dev_kfree_skb_any(skb); + return -EFAULT; + } + cur += rest_len; + skb_pull(skb, rest_len); + if (skb->len) { + mif_info("queue-head, skb->len = %d\n", + skb->len); + skb_queue_head(&iod->sk_rx_q, skb); + } + mif_debug("return %u\n", rest_len); + return rest_len; + } + + copy_len = min(rest_len, skb->len); + if (copy_to_user(cur, skb->data, copy_len) != 0) { dev_kfree_skb_any(skb); return -EFAULT; } - skb_pull(skb, count); - if (skb->len) { - mif_info("queue-head, skb->len = %d\n", - skb->len); - skb_queue_head(&iod->sk_rx_q, skb); - } + cur += skb->len; + dev_kfree_skb_any(skb); + rest_len -= copy_len; - return count; - } else { - mif_err("<%s> skb->len %d > count %d\n", - iod->name, skb->len, count); + if (!rest_len) + break; + + skb = skb_dequeue(&iod->sk_rx_q); + if (!skb) { + mif_err("<%s> %d / %d sk_rx_q\n", iod->name, + (count - rest_len), count); + return count - rest_len; + } + } + } else { + if (skb->len > count) { + mif_err("<%s> skb->len %d > count %d\n", iod->name, + skb->len, count); dev_kfree_skb_any(skb); return -EFAULT; } - } + pktsize = skb->len; + if (copy_to_user(buf, skb->data, pktsize) != 0) { + dev_kfree_skb_any(skb); + return -EFAULT; + } + if (iod->format == IPC_FMT) + mif_debug("copied %d bytes to user\n", pktsize); - pktsize = skb->len; - if (copy_to_user(buf, skb->data, pktsize) != 0) { dev_kfree_skb_any(skb); - return -EFAULT; } - if (iod->format == IPC_FMT) - mif_debug("copied %d bytes to user\n", pktsize); - - dev_kfree_skb_any(skb); - return pktsize; } @@ -1370,6 +1485,7 @@ static int vnet_xmit(struct sk_buff *skb, struct net_device *ndev) struct io_device *iod = vnet->iod; struct link_device *ld = get_current_link(iod); struct raw_hdr hd; + struct iphdr *ip_header = NULL; /* When use `handover' with Network Bridge, * user -> TCP/IP(kernel) -> bridge device -> TCP/IP(kernel) -> this. @@ -1383,9 +1499,17 @@ static int vnet_xmit(struct sk_buff *skb, struct net_device *ndev) skb_pull(skb, sizeof(struct ethhdr)); } + /* ip loop-back */ + ip_header = (struct iphdr *)skb->data; + if (iod->msd->loopback_ipaddr && + ip_header->daddr == iod->msd->loopback_ipaddr) { + swap(ip_header->saddr, ip_header->daddr); + hd.channel = DATA_LOOPBACK_CHANNEL; + } else { + hd.channel = iod->id & 0x1F; + } hd.len = skb->len + sizeof(hd); hd.control = 0; - hd.channel = iod->id & 0x1F; headroom = sizeof(hd) + sizeof(hdlc_start); tailroom = sizeof(hdlc_end); @@ -1523,9 +1647,13 @@ int sipc4_init_io_device(struct io_device *iod) ret = device_create_file(iod->miscdev.this_device, &attr_waketime); if (ret) - mif_err("failed to create sysfs file : %s\n", + mif_err("failed to create `waketime' file : %s\n", + iod->name); + ret = device_create_file(iod->miscdev.this_device, + &attr_loopback); + if (ret) + mif_err("failed to create `loopback file' : %s\n", iod->name); - break; default: diff --git a/drivers/misc/modem_if/sipc4_modem.c b/drivers/misc/modem_if/sipc4_modem.c index 1f9ee7c..59e2de9 100644 --- a/drivers/misc/modem_if/sipc4_modem.c +++ b/drivers/misc/modem_if/sipc4_modem.c @@ -41,7 +41,38 @@ #define RFS_WAKE_TIME (HZ*3) #define RAW_WAKE_TIME (HZ*6) -static struct modem_ctl *create_modemctl_device(struct platform_device *pdev) +static struct modem_shared *create_modem_shared_data(void) +{ + struct modem_shared *msd; + + msd = kzalloc(sizeof(struct modem_shared), GFP_KERNEL); + if (!msd) + return NULL; + + /* initialize link device list */ + INIT_LIST_HEAD(&msd->link_dev_list); + + /* initialize tree of io devices */ + msd->iodevs_tree_chan = RB_ROOT; + msd->iodevs_tree_fmt = RB_ROOT; + + msd->storage.cnt = 0; + msd->storage.addr = + kzalloc(MAX_MIF_BUFF_SIZE + MAX_MIF_SEPA_SIZE, GFP_KERNEL); + if (!msd->storage.addr) { + mif_err("IPC logger buff alloc failed!!\n"); + return NULL; + } + memset(msd->storage.addr, 0, MAX_MIF_BUFF_SIZE); + memcpy(msd->storage.addr, MIF_SEPARATOR, MAX_MIF_SEPA_SIZE); + msd->storage.addr += MAX_MIF_SEPA_SIZE; + spin_lock_init(&msd->lock); + + return msd; +} + +static struct modem_ctl *create_modemctl_device(struct platform_device *pdev, + struct modem_shared *msd) { int ret = 0; struct modem_data *pdata; @@ -53,6 +84,7 @@ static struct modem_ctl *create_modemctl_device(struct platform_device *pdev) if (!modemctl) return NULL; + modemctl->msd = msd; modemctl->dev = dev; modemctl->phone_state = STATE_OFFLINE; @@ -60,13 +92,6 @@ static struct modem_ctl *create_modemctl_device(struct platform_device *pdev) modemctl->mdm_data = pdata; modemctl->name = pdata->name; - /* initialize link device list */ - INIT_LIST_HEAD(&modemctl->commons.link_dev_list); - - /* initialize tree of io devices */ - modemctl->commons.iodevs_tree_chan = RB_ROOT; - modemctl->commons.iodevs_tree_fmt = RB_ROOT; - /* init modemctl device for getting modemctl operations */ ret = call_modem_init_func(modemctl, pdata); if (ret) { @@ -74,24 +99,14 @@ static struct modem_ctl *create_modemctl_device(struct platform_device *pdev) return NULL; } - modemctl->use_mif_log = pdata->use_mif_log; - if (pdata->use_mif_log) { - mif_err("<%s> IPC logger can be used.\n", - pdata->name); - } - - ret = mif_init_log(modemctl); - if (ret < 0) { - kfree(modemctl); - return NULL; - } - mif_info("%s is created!!!\n", pdata->name); + return modemctl; } static struct io_device *create_io_device(struct modem_io_t *io_t, - struct modem_ctl *modemctl, struct modem_data *pdata) + struct modem_shared *msd, struct modem_ctl *modemctl, + struct modem_data *pdata) { int ret = 0; struct io_device *iod = NULL; @@ -124,6 +139,16 @@ static struct io_device *create_io_device(struct modem_io_t *io_t, mif_info("Bood device = %s\n", iod->name); } + /* link between io device and modem shared */ + iod->msd = msd; + + /* add iod to rb_tree */ + if (iod->format != IPC_RAW) + insert_iod_with_format(msd, iod->format, iod); + + if (sipc4_is_not_reserved_channel(iod->id)) + insert_iod_with_channel(msd, iod->id, iod); + /* register misc device or net device */ ret = sipc4_init_io_device(iod); if (ret) { @@ -136,20 +161,13 @@ static struct io_device *create_io_device(struct modem_io_t *io_t, return iod; } -static int attach_devices(struct modem_ctl *mc, struct io_device *iod, - enum modem_link tx_link) +static int attach_devices(struct io_device *iod, enum modem_link tx_link) { + struct modem_shared *msd = iod->msd; struct link_device *ld; - /* add iod to rb_tree */ - if (iod->format != IPC_RAW) - insert_iod_with_format(&mc->commons, iod->format, iod); - - if (sipc4_is_not_reserved_channel(iod->id)) - insert_iod_with_channel(&mc->commons, iod->id, iod); - /* find link type for this io device */ - list_for_each_entry(ld, &mc->commons.link_dev_list, list) { + list_for_each_entry(ld, &msd->link_dev_list, list) { if (IS_CONNECTED(iod, ld)) { /* The count 1 bits of iod->link_types is count * of link devices of this iod. @@ -210,14 +228,21 @@ static int __devinit modem_probe(struct platform_device *pdev) { int i; struct modem_data *pdata = pdev->dev.platform_data; - struct modem_ctl *modemctl; + struct modem_shared *msd = NULL; + struct modem_ctl *modemctl = NULL; struct io_device *iod[pdata->num_iodevs]; struct link_device *ld; mif_err("%s\n", pdev->name); memset(iod, 0, sizeof(iod)); - modemctl = create_modemctl_device(pdev); + msd = create_modem_shared_data(); + if (!msd) { + mif_err("msd == NULL\n"); + goto err_free_modemctl; + } + + modemctl = create_modemctl_device(pdev, msd); if (!modemctl) { mif_err("modemctl == NULL\n"); goto err_free_modemctl; @@ -235,20 +260,21 @@ static int __devinit modem_probe(struct platform_device *pdev) mif_err("link created: %s\n", ld->name); ld->link_type = i; ld->mc = modemctl; - list_add(&ld->list, &modemctl->commons.link_dev_list); + ld->msd = msd; + list_add(&ld->list, &msd->link_dev_list); } } /* create io deivces and connect to modemctl device */ for (i = 0; i < pdata->num_iodevs; i++) { - iod[i] = create_io_device(&pdata->iodevs[i], modemctl, pdata); + iod[i] = create_io_device(&pdata->iodevs[i], msd, modemctl, + pdata); if (!iod[i]) { mif_err("iod[%d] == NULL\n", i); goto err_free_modemctl; } - attach_devices(modemctl, iod[i], - pdata->iodevs[i].tx_link); + attach_devices(iod[i], pdata->iodevs[i].tx_link); } platform_set_drvdata(pdev, modemctl); @@ -264,6 +290,9 @@ err_free_modemctl: if (modemctl != NULL) kfree(modemctl); + if (msd != NULL) + kfree(msd); + return -ENOMEM; } diff --git a/drivers/misc/modem_if/sipc5_io_device.c b/drivers/misc/modem_if/sipc5_io_device.c index 05578f4..a9932c1 100644 --- a/drivers/misc/modem_if/sipc5_io_device.c +++ b/drivers/misc/modem_if/sipc5_io_device.c @@ -69,48 +69,129 @@ static ssize_t store_waketime(struct device *dev, static struct device_attribute attr_waketime = __ATTR(waketime, S_IRUGO | S_IWUSR, show_waketime, store_waketime); -static inline int sipc5_check_frame_cfg(u8 *buff, struct sipc5_frame_data *frm) +static ssize_t show_loopback(struct device *dev, + struct device_attribute *attr, char *buf) { - u8 config = buff[0]; + struct miscdevice *miscdev = dev_get_drvdata(dev); + struct modem_shared *msd = + container_of(miscdev, struct io_device, miscdev)->msd; + unsigned char *ip = (unsigned char *)&msd->loopback_ipaddr; + char *p = buf; - if ((config & SIPC5_START_MASK) != SIPC5_START_MASK) - return -EBADMSG; + p += sprintf(buf, "%u.%u.%u.%u\n", ip[0], ip[1], ip[2], ip[3]); + + return p - buf; +} + +static ssize_t store_loopback(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct miscdevice *miscdev = dev_get_drvdata(dev); + struct modem_shared *msd = + container_of(miscdev, struct io_device, miscdev)->msd; + + msd->loopback_ipaddr = ipv4str_to_be32(buf, count); + + return count; +} + +static struct device_attribute attr_loopback = + __ATTR(loopback, S_IRUGO | S_IWUSR, show_loopback, store_loopback); + +static void iodev_showtxlink(struct io_device *iod, void *args) +{ + char **p = (char **)args; + struct link_device *ld = get_current_link(iod); + + if (iod->io_typ == IODEV_NET && IS_CONNECTED(iod, ld)) + *p += sprintf(*p, "%s: %s\n", iod->name, ld->name); +} + +static ssize_t show_txlink(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct miscdevice *miscdev = dev_get_drvdata(dev); + struct modem_shared *msd = + container_of(miscdev, struct io_device, miscdev)->msd; + char *p = buf; + + iodevs_for_each(msd, iodev_showtxlink, &p); + + return p - buf; +} + +static ssize_t store_txlink(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + /* don't change without gpio dynamic switching */ + return -EINVAL; +} - frm->config = config; - frm->hdr_len = SIPC5_MIN_HEADER_SIZE; +static struct device_attribute attr_txlink = + __ATTR(txlink, S_IRUGO | S_IWUSR, show_txlink, store_txlink); - if (config & SIPC5_PADDING_EXIST) +/** + * rx_check_frame_cfg + * @cfg: configuration field of a link layer header + * @frm: pointer to the sipc5_frame_data buffer + * + * 1) Checks whether or not an extended field exists + * 2) Calculates the length of a link layer header + * + * Returns the size of a link layer header + * + * Must be invoked only when the configuration field of the link layer header + * is validated with sipc5_start_valid() function + */ +static int rx_check_frame_cfg(u8 cfg, struct sipc5_frame_data *frm) +{ + frm->config = cfg; + + if (likely(cfg & SIPC5_PADDING_EXIST)) frm->padding = true; - if (unlikely(config & SIPC5_EXT_FIELD_EXIST)) { - frm->ext_fld = true; - if (config & SIPC5_CTL_FIELD_EXIST) { + if (unlikely(cfg & SIPC5_EXT_FIELD_EXIST)) { + if (cfg & SIPC5_CTL_FIELD_EXIST) { frm->ctl_fld = true; frm->hdr_len = SIPC5_HEADER_SIZE_WITH_CTL_FLD; } else { frm->ext_len = true; frm->hdr_len = SIPC5_HEADER_SIZE_WITH_EXT_LEN; } + } else { + frm->hdr_len = SIPC5_MIN_HEADER_SIZE; } - return SIPC5_CONFIG_SIZE; + return frm->hdr_len; } -static inline void sipc5_build_rx_frame_data(struct link_device *ld, +/** + * rx_build_meta_data + * @ld: pointer to the link device + * @frm: pointer to the sipc5_frame_data buffer + * + * Fills each field of sipc5_frame_data from a link layer header + * 1) Extracts the channel ID + * 2) Calculates the length of a link layer frame + * 3) Extracts a control field if exists + * 4) Calculates the length of an IPC message packet in the link layer frame + * + */ +static void rx_build_meta_data(struct link_device *ld, struct sipc5_frame_data *frm) { u16 *sz16 = (u16 *)(frm->hdr + SIPC5_LEN_OFFSET); u32 *sz32 = (u32 *)(frm->hdr + SIPC5_LEN_OFFSET); frm->ch_id = frm->hdr[SIPC5_CH_ID_OFFSET]; - frm->len = *sz16; - if (unlikely(frm->ext_fld)) { - if (frm->ctl_fld) - frm->control = frm->hdr[SIPC5_CTL_OFFSET]; - else - frm->len = *sz32; - } + if (unlikely(frm->ext_len)) + frm->len = *sz32; + else + frm->len = *sz16; + + if (unlikely(frm->ctl_fld)) + frm->control = frm->hdr[SIPC5_CTL_OFFSET]; frm->data_len = frm->len - frm->hdr_len; @@ -118,114 +199,83 @@ static inline void sipc5_build_rx_frame_data(struct link_device *ld, ld->name, frm->ch_id, frm->len, frm->control, frm->data_len); } -static inline struct sk_buff *sipc5_prepare_rx_skb(struct io_device *iod, - struct link_device *ld, unsigned len) -{ - struct sk_buff *skb; - - if (iod->format == IPC_MULTI_RAW && iod->use_handover) { - int alloc = len + sizeof(struct ethhdr); - skb = rx_alloc_skb(alloc, GFP_ATOMIC, iod, ld); - if (unlikely(!skb)) - return NULL; - skb_reserve(skb, sizeof(struct ethhdr)); - } else { - skb = rx_alloc_skb(len, GFP_ATOMIC, iod, ld); - } - - return skb; -} - -/* Check and store link layer header, then alloc an skb */ -static int sipc5_recv_header(struct io_device *iod, struct link_device *ld, - u8 *buff, unsigned size, struct sipc5_frame_data *frm) +/** + * tx_build_link_header + * @frm: pointer to the sipc5_frame_data buffer + * @iod: pointer to the IO device + * @ld: pointer to the link device + * @count: length of the data to be transmitted + * + * Builds the meta data for an SIPC5 frame and the link layer header of it + * Returns the link layer header length for an SIPC5 frame or 0 for other frame + */ +static unsigned tx_build_link_header(struct sipc5_frame_data *frm, + struct io_device *iod, struct link_device *ld, ssize_t count) { - int len = 0; - - mif_debug("%s: size %d\n", ld->name, size); + u8 *buff = frm->hdr; + u16 *sz16 = (u16 *)(buff + SIPC5_LEN_OFFSET); + u32 *sz32 = (u32 *)(buff + SIPC5_LEN_OFFSET); - if (likely(!frm->config)) { - len = sipc5_check_frame_cfg(buff, frm); - if (len < 0) { - mif_info("%s: ERR! wrong start (0x%02x)\n", - ld->name, buff[0]); - return len; - } + memset(frm, 0, sizeof(struct sipc5_frame_data)); - /* Copy the link layer header to the header buffer */ - len = min(frm->hdr_len, size); - memcpy(frm->hdr, buff, len); - } else { - /* Copy the link layer header to the header buffer */ - len = min((frm->hdr_len - frm->hdr_rcvd), size); - memcpy((frm->hdr + frm->hdr_rcvd), buff, len); + if (iod->format == IPC_CMD || + iod->format == IPC_BOOT || + iod->format == IPC_RAMDUMP) { + frm->len = count; + return 0; } - frm->hdr_rcvd += len; + frm->config = SIPC5_START_MASK; - mif_debug("%s: FRM hdr.len:%d hdr.rcvd:%d\n", - ld->name, frm->hdr_len, frm->hdr_rcvd); + if (iod->format == IPC_FMT && count > 2048) { + frm->ctl_fld = true; + frm->config |= SIPC5_EXT_FIELD_EXIST; + frm->config |= SIPC5_CTL_FIELD_EXIST; + } - if (frm->hdr_rcvd >= frm->hdr_len) { - struct sk_buff *skb; - sipc5_build_rx_frame_data(ld, frm); - skb = sipc5_prepare_rx_skb(iod, ld, frm->data_len); - fragdata(iod, ld)->skb_recv = skb; + if (iod->id >= SIPC5_CH_ID_RFS_0 && count > 0xFFFF) { + frm->ext_len = true; + frm->config |= SIPC5_EXT_FIELD_EXIST; } - return len; -} + if (ld->aligned) + frm->config |= SIPC5_PADDING_EXIST; -/* copy data to skb */ -static int sipc5_recv_payload(struct io_device *iod, struct link_device *ld, - u8 *buff, unsigned size, struct sipc5_frame_data *frm) -{ - struct sk_buff *skb = fragdata(iod, ld)->skb_recv; - unsigned rest = frm->data_len - frm->data_rcvd; - unsigned len; + frm->ch_id = iod->id; - /* - ** rest == frm->data_len - frm->data_rcvd == tailroom of skb or mifb - */ - rest = frm->data_len - frm->data_rcvd; - mif_debug("%s: FRM data.len:%d data.rcvd:%d rest:%d size:%d\n", - ld->name, frm->data_len, frm->data_rcvd, rest, size); + frm->hdr_len = sipc5_get_hdr_len(frm->config); + frm->data_len = count; + frm->len = frm->hdr_len + frm->data_len; - /* If there is no skb, data must be dropped. */ - len = min(rest, size); - if (skb) - memcpy(skb_put(skb, len), buff, len); + buff[SIPC5_CONFIG_OFFSET] = frm->config; + buff[SIPC5_CH_ID_OFFSET] = frm->ch_id; - frm->data_rcvd += len; + if (unlikely(frm->ext_len)) + *sz32 = (u32)frm->len; + else + *sz16 = (u16)frm->len; - mif_debug("%s: FRM data.len:%d data.rcvd:%d\n", - ld->name, frm->data_len, frm->data_rcvd); + if (unlikely(frm->ctl_fld)) + buff[SIPC5_CTL_OFFSET] = frm->control; - return len; + return frm->hdr_len; } -static int sipc5_recv_fmt(struct sk_buff *skb, struct sipc5_frame_data *frm) +static int rx_fmt_frame(struct sk_buff *skb) { struct io_device *iod = skbpriv(skb)->iod; struct link_device *ld = skbpriv(skb)->ld; - struct sk_buff_head *rxq; + struct sk_buff_head *rxq = &iod->sk_rx_q; struct sipc_fmt_hdr *fh; struct sk_buff *rx_skb; - unsigned id; - - rxq = &iod->sk_rx_q; - if (!rxq) { - mif_debug("%s: no sk_rx_q\n", iod->name); - return -EINVAL; - } - - id = frm->control & 0x7F; + u8 ctrl = skbpriv(skb)->control; + unsigned id = ctrl & 0x7F; if (iod->skb[id] == NULL) { /* ** There has been no multiple frame with this ID. */ - if ((frm->control & 0x80) == 0) { + if ((ctrl & 0x80) == 0) { /* ** It is a single frame because the "more" bit is 0. */ @@ -252,7 +302,7 @@ static int sipc5_recv_fmt(struct sk_buff *skb, struct sipc5_frame_data *frm) mif_debug("%s: start multi-frame (ID:%d len:%d)\n", iod->name, id, fh->len); - rx_skb = rx_alloc_skb(fh->len, GFP_ATOMIC, iod, ld); + rx_skb = rx_alloc_skb(fh->len, iod, ld); if (!rx_skb) { mif_info("%s: ERR! rx_alloc_skb fail\n", iod->name); return -ENOMEM; @@ -269,7 +319,7 @@ static int sipc5_recv_fmt(struct sk_buff *skb, struct sipc5_frame_data *frm) memcpy(skb_put(rx_skb, skb->len), skb->data, skb->len); dev_kfree_skb_any(skb); - if (frm->control & 0x80) { + if (ctrl & 0x80) { /* The last frame has not arrived yet. */ mif_debug("%s: recv multi-frame (ID:%d rcvd:%d)\n", iod->name, id, rx_skb->len); @@ -295,7 +345,7 @@ static int sipc5_recv_fmt(struct sk_buff *skb, struct sipc5_frame_data *frm) return 0; } -static int sipc5_recv_rfs(struct sk_buff *skb) +static int rx_rfs_frame(struct sk_buff *skb) { struct io_device *iod = skbpriv(skb)->iod; struct sk_buff_head *rxq = &iod->sk_rx_q; @@ -303,8 +353,7 @@ static int sipc5_recv_rfs(struct sk_buff *skb) skb_queue_tail(rxq, skb); if (unlikely(rxq->qlen > 2048)) { struct sk_buff *victim; - mif_debug("%s: ERR! rxq->qlen %d > 2048\n", - iod->name, rxq->qlen); + mif_debug("%s: rxq->qlen %d > 2048\n", iod->name, rxq->qlen); victim = skb_dequeue(rxq); dev_kfree_skb_any(victim); } else { @@ -316,7 +365,42 @@ static int sipc5_recv_rfs(struct sk_buff *skb) return 0; } -static int sipc5_recv_misc(struct sk_buff *skb) +static int rx_loopback(struct sk_buff *skb) +{ + struct io_device *iod = skbpriv(skb)->iod; + struct link_device *ld = get_current_link(iod); + struct sipc5_frame_data frm; + unsigned headroom; + unsigned tailroom = 0; + int ret; + + headroom = tx_build_link_header(&frm, iod, ld, skb->len); + + if (ld->aligned) + tailroom = sipc5_calc_padding_size(headroom + skb->len); + + /* We need not to expand skb in here. dev_alloc_skb (in rx_alloc_skb) + * already alloc 32bytes padding in headroom. 32bytes are enough. + */ + + /* store IPC link header to start of skb + * this is skb_push not skb_put. different with misc_write. + */ + memcpy(skb_push(skb, headroom), frm.hdr, headroom); + + /* store padding */ + if (tailroom) + skb_put(skb, tailroom); + + /* forward */ + ret = ld->send(ld, iod, skb); + if (ret < 0) + mif_err("%s->%s: ld->send fail: %d\n", iod->name, + ld->name, ret); + return ret; +} + +static int rx_raw_misc(struct sk_buff *skb) { struct io_device *iod = skbpriv(skb)->iod; /* same with real_iod */ struct sk_buff_head *rxq = &iod->sk_rx_q; @@ -324,8 +408,7 @@ static int sipc5_recv_misc(struct sk_buff *skb) skb_queue_tail(rxq, skb); if (unlikely(rxq->qlen > 2048)) { struct sk_buff *victim; - mif_debug("%s: ERR! rxq->qlen %d > 2048\n", - iod->name, rxq->qlen); + mif_debug("%s: rxq->qlen %d > 2048\n", iod->name, rxq->qlen); victim = skb_dequeue(rxq); dev_kfree_skb_any(victim); } else { @@ -337,7 +420,7 @@ static int sipc5_recv_misc(struct sk_buff *skb) return 0; } -static int sipc5_recv_pdp(struct sk_buff *skb) +static int rx_multi_pdp(struct sk_buff *skb) { struct io_device *iod = skbpriv(skb)->iod; /* same with real_iod */ struct net_device *ndev; @@ -386,69 +469,24 @@ static int sipc5_recv_pdp(struct sk_buff *skb) return ret; } -/** rx_iodev_work - rx workqueue for raw data - * - * @work: workqueue - * - * If you throw packets to Network layer directly in interrupt context, - * sometimes, you'll meet backlog buffer full of Network layer. - * Applications need some time to get packets from Network layer. - * And, we need to retry logic when NET_RX_DROP occured. this work ensure - * retry when netif_rx failed. - */ -static void rx_iodev_work(struct work_struct *work) -{ - int ret = 0; - struct sk_buff *skb = NULL; - struct io_device *iod = container_of(work, struct io_device, - rx_work.work); - - while ((skb = skb_dequeue(&iod->sk_rx_q)) != NULL) { - ret = sipc5_recv_pdp(skb); - if (ret < 0) { - mif_err("%s: sipc5_recv_pdp fail (err %d)", - iod->name, ret); - dev_kfree_skb_any(skb); - } else if (ret == NET_RX_DROP) { - mif_err("%s: ret == NET_RX_DROP. retry later.\n", - iod->name); - schedule_delayed_work(&iod->rx_work, - msecs_to_jiffies(100)); - return; - } - } -} - -static int rx_multipdp(struct sk_buff *skb) -{ - /* in sipc5, this iod == real_iod. not MULTIPDP's iod */ - struct io_device *iod = skbpriv(skb)->iod; - - if (iod->io_typ != IODEV_NET) { - mif_info("%s: ERR! wrong io_type %d\n", iod->name, iod->io_typ); - return -EINVAL; - } - - skb_queue_tail(&iod->sk_rx_q, skb); - mif_debug("%s: sk_rx_qlen %d\n", iod->name, iod->sk_rx_q.qlen); - - schedule_delayed_work(&iod->rx_work, 0); - return 0; -} - -static int sipc5_recv_demux(struct link_device *ld, struct sk_buff *skb, - struct sipc5_frame_data *frm) +static int rx_demux(struct link_device *ld, struct sk_buff *skb) { struct io_device *iod = NULL; + char *link = ld->name; + u8 ch = skbpriv(skb)->ch_id; - if (unlikely(frm->ch_id >= SIPC5_CH_ID_MAX || frm->ch_id == 0)) { - mif_info("%s: ERR! invalid channel %d\n", ld->name, frm->ch_id); + if (unlikely(ch == SIPC5_CH_ID_MAX || ch == 0)) { + mif_info("%s: ERR! invalid ch# %d\n", link, ch); return -ENODEV; } - iod = link_get_iod_with_channel(ld, frm->ch_id); + /* IP loopback */ + if (ch == DATA_LOOPBACK_CHANNEL && ld->msd->loopback_ipaddr) + ch = RMNET0_CH_ID; + + iod = link_get_iod_with_channel(ld, ch); if (unlikely(!iod)) { - mif_info("%s: ERR! no iod for ch# %d\n", ld->name, frm->ch_id); + mif_info("%s: ERR! no iod for ch# %d\n", link, ch); return -ENODEV; } @@ -456,54 +494,127 @@ static int sipc5_recv_demux(struct link_device *ld, struct sk_buff *skb, skbpriv(skb)->iod = iod; skbpriv(skb)->real_iod = iod; + /* don't care about CP2AP_LOOPBACK_CHANNEL is opened */ + if (unlikely(iod->id == CP2AP_LOOPBACK_CHANNEL)) + return rx_loopback(skb); + if (atomic_read(&iod->opened) <= 0) { - mif_info("%s: ERR! %s is not opened\n", ld->name, iod->name); + mif_info("%s: ERR! %s is not opened\n", link, iod->name); return -ENODEV; } - if (frm->ch_id >= SIPC5_CH_ID_RFS_0) - return sipc5_recv_rfs(skb); - else if (frm->ch_id >= SIPC5_CH_ID_FMT_0) - return sipc5_recv_fmt(skb, frm); + if (ch >= SIPC5_CH_ID_RFS_0) + return rx_rfs_frame(skb); + else if (ch >= SIPC5_CH_ID_FMT_0) + return rx_fmt_frame(skb); else if (iod->io_typ == IODEV_MISC) - return sipc5_recv_misc(skb); + return rx_raw_misc(skb); else - return rx_multipdp(skb); + return rx_multi_pdp(skb); +} + +/* Check and store link layer header, then alloc an skb */ +static int rx_header_from_serial(struct io_device *iod, struct link_device *ld, + u8 *buff, unsigned size, struct sipc5_frame_data *frm) +{ + char *link = ld->name; + struct sk_buff *skb; + int len; + u8 cfg = buff[0]; + + mif_debug("%s: size %d\n", link, size); + + if (!frm->config) { + if (unlikely(!sipc5_start_valid(cfg))) { + mif_info("%s: ERR! wrong start (0x%02x)\n", link, cfg); + return -EBADMSG; + } + rx_check_frame_cfg(cfg, frm); + + /* Copy the link layer header to the header buffer */ + len = min(frm->hdr_len, size); + memcpy(frm->hdr, buff, len); + } else { + /* Copy the link layer header to the header buffer */ + len = min((frm->hdr_len - frm->hdr_rcvd), size); + memcpy((frm->hdr + frm->hdr_rcvd), buff, len); + } + + frm->hdr_rcvd += len; + + mif_debug("%s: FRM hdr_len:%d, hdr_rcvd:%d\n", + link, frm->hdr_len, frm->hdr_rcvd); + + if (frm->hdr_rcvd >= frm->hdr_len) { + rx_build_meta_data(ld, frm); + skb = rx_alloc_skb(frm->data_len, iod, ld); + fragdata(iod, ld)->skb_recv = skb; + skbpriv(skb)->ch_id = frm->ch_id; + skbpriv(skb)->control = frm->control; + } + + return len; +} + +/* copy data to skb */ +static int rx_payload_from_serial(struct io_device *iod, struct link_device *ld, + u8 *buff, unsigned size, struct sipc5_frame_data *frm) +{ + struct sk_buff *skb = fragdata(iod, ld)->skb_recv; + char *link = ld->name; + unsigned rest = frm->data_len - frm->data_rcvd; + unsigned len; + + /* rest == (frm->data_len - frm->data_rcvd) == tailroom of skb */ + rest = frm->data_len - frm->data_rcvd; + mif_debug("%s: FRM data.len:%d data.rcvd:%d rest:%d size:%d\n", + link, frm->data_len, frm->data_rcvd, rest, size); + + /* If there is no skb, data must be dropped. */ + len = min(rest, size); + if (skb) + memcpy(skb_put(skb, len), buff, len); + + frm->data_rcvd += len; + + mif_debug("%s: FRM data_len:%d, data_rcvd:%d\n", + link, frm->data_len, frm->data_rcvd); + + return len; } -static int sipc5_recv_ipc_from_serial(struct io_device *iod, - struct link_device *ld, const char *data, unsigned size) +static int rx_frame_from_serial(struct io_device *iod, struct link_device *ld, + const char *data, unsigned size) { struct sipc5_frame_data *frm = &fragdata(iod, ld)->f_data; struct sk_buff *skb; + char *link = ld->name; u8 *buff = (u8 *)data; int rest = (int)size; int err = 0; int done = 0; - mif_debug("%s: size = %d\n", ld->name, size); + mif_debug("%s: size = %d\n", link, size); if (frm->hdr_rcvd >= frm->hdr_len && frm->data_rcvd < frm->data_len) { /* - There may be an skb or mifb (fragdata(iod, ld)->skb_recv) that - is waiting for more IPC frame. In this case, sipc5_recv_header - function must be skipped. + ** There is an skb that is waiting for more SIPC5 data. + ** In this case, rx_header_from_serial() must be skipped. */ mif_debug("%s: FRM data.len:%d data.rcvd:%d -> recv_data\n", - ld->name, frm->data_len, frm->data_rcvd); + link, frm->data_len, frm->data_rcvd); goto recv_data; } next_frame: /* Receive and analyze header, then prepare an akb */ - err = done = sipc5_recv_header(iod, ld, buff, rest, frm); + err = done = rx_header_from_serial(iod, ld, buff, rest, frm); if (err < 0) goto err_exit; buff += done; rest -= done; - mif_debug("%s: sipc5_recv_header() -> done:%d rest:%d\n", - ld->name, done, rest); + mif_debug("%s: rx_header() -> done:%d rest:%d\n", link, done, rest); if (rest < 0) goto err_range; @@ -513,15 +624,13 @@ next_frame: recv_data: err = 0; - mif_debug("%s: done:%d rest:%d -> recv_payload()\n", - ld->name, done, rest); + mif_debug("%s: done:%d rest:%d -> rx_payload()\n", link, done, rest); - done = sipc5_recv_payload(iod, ld, buff, rest, frm); + done = rx_payload_from_serial(iod, ld, buff, rest, frm); buff += done; rest -= done; - mif_debug("%s: recv_payload() -> done:%d rest:%d\n", - ld->name, done, rest); + mif_debug("%s: rx_payload() -> done:%d rest:%d\n", link, done, rest); if (rest == 0 && frm->data_rcvd < frm->data_len) { /* @@ -538,14 +647,14 @@ recv_data: done = sipc5_calc_padding_size(frm->len); if (done > rest) { mif_info("%s: ERR! padding %d > rest %d\n", - ld->name, done, rest); + link, done, rest); goto err_exit; } buff += done; rest -= done; - mif_debug("%s: padding:%d -> rest:%d\n", ld->name, done, rest); + mif_debug("%s: padding:%d -> rest:%d\n", link, done, rest); if (rest < 0) goto err_range; @@ -554,12 +663,12 @@ recv_data: skb = fragdata(iod, ld)->skb_recv; if (likely(skb)) { - mif_debug("%s: len %d -> recv_demux()\n", ld->name, skb->len); - err = sipc5_recv_demux(ld, skb, frm); + mif_debug("%s: len:%d -> rx_demux()\n", link, skb->len); + err = rx_demux(ld, skb); if (err < 0) dev_kfree_skb_any(skb); } else { - mif_debug("%s: len:%d -> drop\n", ld->name, skb->len); + mif_debug("%s: len:%d -> drop\n", link, skb->len); } /* initialize the skb_recv and the frame_data buffer */ @@ -578,44 +687,61 @@ err_exit: dev_kfree_skb_any(fragdata(iod, ld)->skb_recv); memset(frm, 0, sizeof(struct sipc5_frame_data)); fragdata(iod, ld)->skb_recv = NULL; - mif_info("%s: ERR! clear frag\n", ld->name); + mif_info("%s: ERR! clear frag\n", link); } return err; err_range: - mif_info("%s: ERR! size:%d vs. rest:%d\n", ld->name, size, rest); + mif_info("%s: ERR! size:%d vs. rest:%d\n", link, size, rest); return size; } -/* Check and store link layer header */ -static int sipc5_recv_header_from_dpram(struct link_device *ld, u8 *buff, +/** + * rx_header_from_mem + * @ld: pointer to the link device + * @buff: pointer to the frame + * @rest: size of the frame + * @frm: pointer to the sipc5_frame_data buffer + * + * 1) Verifies a link layer header configuration of a frame + * 2) Stores the link layer header to the header buffer + * 3) Builds and stores the meta data of the frame into a meta data buffer + * 4) Verifies the length of the frame + * + * Returns SIPC5 header length + */ +static int rx_header_from_mem(struct link_device *ld, u8 *buff, unsigned rest, struct sipc5_frame_data *frm) { - int len = sipc5_check_frame_cfg(buff, frm); + char *link = ld->name; + u8 cfg = buff[0]; - if (len < 0) { - mif_info("%s: ERR! wrong start 0x%02x\n", - ld->name, buff[0]); - return len; - } else if (len > SIPC5_MAX_HEADER_SIZE) { - mif_info("%s: ERR! wrong header length %d\n", - ld->name, len); + /* Verify link layer header configuration */ + if (unlikely(!sipc5_start_valid(cfg))) { + mif_info("%s: ERR! wrong start (0x%02x)\n", link, cfg); return -EBADMSG; } + rx_check_frame_cfg(cfg, frm); - /* Copy the link layer header to the header buffer */ - len = frm->hdr_len; - memcpy(frm->hdr, buff, len); + /* Store the link layer header to the header buffer */ + memcpy(frm->hdr, buff, frm->hdr_len); frm->hdr_rcvd = frm->hdr_len; - sipc5_build_rx_frame_data(ld, frm); + /* Build and store the meta data of this frame */ + rx_build_meta_data(ld, frm); - return len; + /* Verify frame length */ + if (unlikely(frm->len > rest)) { + mif_info("%s: ERR! frame length %d > rest %d\n", + link, frm->len, rest); + return -EBADMSG; + } + + return frm->hdr_rcvd; } /* copy data to skb */ -static int sipc5_recv_payload_from_dpram(struct sk_buff *skb, u8 *buff, - unsigned len) +static int rx_payload_from_mem(struct sk_buff *skb, u8 *buff, unsigned len) { /* If there is no skb, data must be dropped. */ if (skb) @@ -623,60 +749,81 @@ static int sipc5_recv_payload_from_dpram(struct sk_buff *skb, u8 *buff, return len; } -static int sipc5_recv_ipc_from_dpram(struct io_device *iod, - struct link_device *ld, const char *data, unsigned size) +static int rx_frame_from_mem(struct io_device *iod, struct link_device *ld, + const char *data, unsigned size) { struct sipc5_frame_data *frm = &fragdata(iod, ld)->f_data; struct sk_buff *skb; + char *link = ld->name; u8 *buff = (u8 *)data; int rest = (int)size; int len; int done; - mif_debug("%s: size = %d\n", ld->name, size); + mif_debug("%s: size = %d\n", link, size); while (rest > 0) { /* Initialize the frame data buffer */ memset(frm, 0, sizeof(struct sipc5_frame_data)); + skb = NULL; /* Receive and analyze link layer header */ - done = sipc5_recv_header_from_dpram(ld, buff, frm); + done = rx_header_from_mem(ld, buff, rest, frm); if (unlikely(done < 0)) return -EBADMSG; + /* Verify rest size */ rest -= done; - if (rest < 0) + if (rest < 0) { + mif_info("%s: ERR! rx_header -> rest %d\n", link, rest); return -ERANGE; + } + + /* Move buff pointer to the payload */ buff += done; /* Prepare an akb */ len = frm->data_len; - skb = sipc5_prepare_rx_skb(iod, ld, len); + skb = rx_alloc_skb(len, iod, ld); + + /* Store channel ID and control fields to the CB of the skb */ + skbpriv(skb)->ch_id = frm->ch_id; + skbpriv(skb)->control = frm->control; /* Receive payload */ - mif_debug("%s: done:%d rest:%d len:%d -> recv_payload()\n", - ld->name, done, rest, len); - done = sipc5_recv_payload_from_dpram(skb, buff, len); + mif_debug("%s: done:%d rest:%d len:%d -> rx_payload()\n", + link, done, rest, len); + done = rx_payload_from_mem(skb, buff, len); rest -= done; - if (rest < 0) + if (rest < 0) { + mif_info("%s: ERR! rx_payload() -> rest %d\n", + link, rest); + if (skb) + dev_kfree_skb_any(skb); return -ERANGE; + } buff += done; /* A padding size is applied to access the next IPC frame. */ if (frm->padding) { done = sipc5_calc_padding_size(frm->len); - rest -= done; - if (rest < 0) + if (done > rest) { + mif_info("%s: ERR! padding %d > rest %d\n", + link, done, rest); + if (skb) + dev_kfree_skb_any(skb); return -ERANGE; + } buff += done; + rest -= done; } if (likely(skb)) { - mif_debug("%s: len:%d -> demux\n", ld->name, skb->len); - if (sipc5_recv_demux(ld, skb, frm) < 0) + mif_debug("%s: len:%d -> rx_demux()\n", link, skb->len); + if (rx_demux(ld, skb) < 0) dev_kfree_skb_any(skb); } else { - mif_debug("%s: len:%d -> drop\n", ld->name, skb->len); + mif_debug("%s: len:%d -> drop\n", link, skb->len); } } @@ -689,26 +836,16 @@ static int io_dev_recv_data_from_link_dev(struct io_device *iod, { struct sk_buff_head *rxq = &iod->sk_rx_q; struct sk_buff *skb; + char *link = ld->name; int err; - if (!ld) { - mif_info("ERR: !ld\n"); - return -EINVAL; - } - - if (!iod) { - mif_info("%s: ERR! !iod\n", ld->name); - return -EINVAL; - } - if (!data) { - mif_info("%s: ERR! !data\n", ld->name); + mif_info("%s: ERR! !data\n", link); return -EINVAL; } if (len <= 0) { - mif_info("%s: ERR! len = %d <= 0\n", - ld->name, len); + mif_info("%s: ERR! len %d <= 0\n", link, len); return -EINVAL; } @@ -721,13 +858,13 @@ static int io_dev_recv_data_from_link_dev(struct io_device *iod, wake_lock_timeout(&iod->wakelock, iod->waketime); if (ld->link_type == LINKDEV_DPRAM && ld->aligned) - err = sipc5_recv_ipc_from_dpram(iod, ld, data, len); + err = rx_frame_from_mem(iod, ld, data, len); else - err = sipc5_recv_ipc_from_serial(iod, ld, data, len); + err = rx_frame_from_serial(iod, ld, data, len); if (err < 0) - mif_info("%s: ERR! sipc5_recv_ipc_from_link fail " - "(err %d)\n", ld->name, err); + mif_info("%s: ERR! rx_frame_from_link fail (err %d)\n", + link, err); return err; @@ -735,13 +872,13 @@ static int io_dev_recv_data_from_link_dev(struct io_device *iod, case IPC_BOOT: case IPC_RAMDUMP: /* save packet to sk_buff */ - skb = rx_alloc_skb(len, GFP_ATOMIC, iod, ld); + skb = rx_alloc_skb(len, iod, ld); if (!skb) { - mif_info("%s: ERR! rx_alloc_skb fail\n", ld->name); + mif_info("%s: ERR! rx_alloc_skb fail\n", link); return -ENOMEM; } - mif_debug("%s: len:%d -> iod:%s\n", ld->name, len, iod->name); + mif_debug("%s: len:%d -> iod:%s\n", link, len, iod->name); memcpy(skb_put(skb, len), data, len); skb_queue_tail(rxq, skb); @@ -757,68 +894,84 @@ static int io_dev_recv_data_from_link_dev(struct io_device *iod, return len; default: - mif_info("%s: ERR! unknown format %d\n", ld->name, iod->format); + mif_info("%s: ERR! unknown format %d\n", link, iod->format); return -EINVAL; } } -static unsigned sipc5_build_tx_link_header(struct sipc5_frame_data *frm, - struct io_device *iod, struct link_device *ld, ssize_t count) +static int rx_frame_from_skb(struct io_device *iod, struct link_device *ld, + struct sk_buff *skb) { - u8 *buff = frm->hdr; - u16 *sz16 = (u16 *)(buff + SIPC5_LEN_OFFSET); - u32 *sz32 = (u32 *)(buff + SIPC5_LEN_OFFSET); + struct sipc5_frame_data *frm = &fragdata(iod, ld)->f_data; + u8 cfg = skb->data[0]; + /* Initialize the frame data buffer */ memset(frm, 0, sizeof(struct sipc5_frame_data)); - if (iod->format == IPC_CMD || - iod->format == IPC_BOOT || - iod->format == IPC_RAMDUMP) { - frm->len = count; - return 0; - } + /* + ** The start of a link layer header has already been checked in the + ** link device. + */ - frm->config = SIPC5_START_MASK; + /* Analyze the configuration of the link layer header */ + rx_check_frame_cfg(cfg, frm); - if (iod->format == IPC_FMT && count > 2048) { - frm->ext_fld = true; - frm->ctl_fld = true; + /* Store the link layer header to the header buffer */ + memcpy(frm->hdr, skb->data, frm->hdr_len); + frm->hdr_rcvd = frm->hdr_len; - frm->config |= SIPC5_EXT_FIELD_EXIST; - frm->config |= SIPC5_CTL_FIELD_EXIST; - } + /* Build and store the meta data of this frame */ + rx_build_meta_data(ld, frm); - if (iod->id >= SIPC5_CH_ID_RFS_0 && count > 0xFFFF) { - frm->ext_fld = true; - frm->ext_len = true; + /* + ** The length of the frame has already been checked in the link device. + */ - frm->config |= SIPC5_EXT_FIELD_EXIST; - } + /* Trim the link layer header off the frame */ + skb_pull(skb, frm->hdr_len); - if (ld->aligned) - frm->config |= SIPC5_PADDING_EXIST; + /* Store channel ID and control fields to the CB of the skb */ + skbpriv(skb)->ch_id = frm->ch_id; + skbpriv(skb)->control = frm->control; - frm->ch_id = iod->id; + /* Demux the frame */ + if (rx_demux(ld, skb) < 0) { + mif_err("%s: ERR! rx_demux fail\n", ld->name); + return -EINVAL; + } - frm->hdr_len = sipc5_get_hdr_size(frm->config); - frm->data_len = count; - frm->len = frm->hdr_len + frm->data_len; + return 0; +} - buff[SIPC5_CONFIG_OFFSET] = frm->config; - buff[SIPC5_CH_ID_OFFSET] = frm->ch_id; +/* called from link device when a packet arrives for this io device */ +static int io_dev_recv_skb_from_link_dev(struct io_device *iod, + struct link_device *ld, struct sk_buff *skb) +{ + char *link = ld->name; + enum dev_format dev = iod->format; + int err; - if (frm->ext_fld) { - if (frm->ctl_fld) { - *sz16 = (u16)frm->len; - buff[SIPC5_CTL_OFFSET] = frm->control; - } else { - *sz32 = (u32)frm->len; + switch (dev) { + case IPC_FMT: + case IPC_RAW: + case IPC_RFS: + case IPC_MULTI_RAW: + if (iod->waketime) + wake_lock_timeout(&iod->wakelock, iod->waketime); + + err = rx_frame_from_skb(iod, ld, skb); + if (err < 0) { + dev_kfree_skb_any(skb); + mif_info("%s: ERR! rx_frame_from_skb fail (err %d)\n", + link, err); } - } else { - *sz16 = (u16)frm->len; - } - return frm->hdr_len; + return err; + + default: + mif_info("%s: ERR! unknown device %d\n", link, dev); + return -EINVAL; + } } /* inform the IO device that the modem is now online or offline or @@ -858,18 +1011,25 @@ static void io_dev_sim_state_changed(struct io_device *iod, bool sim_online) } } +static void iodev_dump_status(struct io_device *iod, void *args) +{ + if (iod->format == IPC_RAW && iod->io_typ == IODEV_NET) { + struct link_device *ld = get_current_link(iod); + mif_com_log(iod->mc->msd, "%s: %s\n", iod->name, ld->name); + } +} + static int misc_open(struct inode *inode, struct file *filp) { struct io_device *iod = to_io_device(filp->private_data); - struct mif_common *commons = &iod->mc->commons; + struct modem_shared *msd = iod->msd; struct link_device *ld; int ret; filp->private_data = (void *)iod; - mif_info("%s\n", iod->name); atomic_inc(&iod->opened); - list_for_each_entry(ld, &commons->link_dev_list, list) { + list_for_each_entry(ld, &msd->link_dev_list, list) { if (IS_CONNECTED(iod, ld) && ld->init_comm) { ret = ld->init_comm(ld, iod); if (ret < 0) { @@ -880,24 +1040,27 @@ static int misc_open(struct inode *inode, struct file *filp) } } + mif_err("%s (opened %d)\n", iod->name, atomic_read(&iod->opened)); + return 0; } static int misc_release(struct inode *inode, struct file *filp) { struct io_device *iod = (struct io_device *)filp->private_data; - struct mif_common *commons = &iod->mc->commons; + struct modem_shared *msd = iod->msd; struct link_device *ld; - mif_info("%s\n", iod->name); atomic_dec(&iod->opened); skb_queue_purge(&iod->sk_rx_q); - list_for_each_entry(ld, &commons->link_dev_list, list) { + list_for_each_entry(ld, &msd->link_dev_list, list) { if (IS_CONNECTED(iod, ld) && ld->terminate_comm) ld->terminate_comm(ld, iod); } + mif_err("%s (opened %d)\n", iod->name, atomic_read(&iod->opened)); + return 0; } @@ -930,6 +1093,8 @@ static long misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) struct io_device *iod = (struct io_device *)filp->private_data; struct link_device *ld = get_current_link(iod); char cpinfo_buf[530] = "CP Crash "; + unsigned long size; + int ret; switch (cmd) { case IOCTL_MODEM_ON: @@ -952,9 +1117,12 @@ static long misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) mif_info("%s: IOCTL_MODEM_BOOT_OFF\n", iod->name); return iod->mc->ops.modem_boot_off(iod->mc); - case IOCTL_MODEM_START: - mif_info("%s: IOCTL_MODEM_START\n", iod->name); - return 0; + case IOCTL_MODEM_BOOT_DONE: + mif_err("%s: IOCTL_MODEM_BOOT_DONE\n", iod->name); + if (iod->mc->ops.modem_boot_done) + return iod->mc->ops.modem_boot_done(iod->mc); + else + return 0; case IOCTL_MODEM_STATUS: mif_debug("%s: IOCTL_MODEM_STATUS\n", iod->name); @@ -983,7 +1151,7 @@ static long misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) if (iod->format != IPC_MULTI_RAW) return -EINVAL; - iodevs_for_each(&iod->mc->commons, iodev_netif_stop, 0); + iodevs_for_each(iod->msd, iodev_netif_stop, 0); return 0; case IOCTL_MODEM_PROTOCOL_RESUME: @@ -993,7 +1161,7 @@ static long misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) if (iod->format != IPC_MULTI_RAW) return -EINVAL; - iodevs_for_each(&iod->mc->commons, iodev_netif_wake, 0); + iodevs_for_each(iod->msd, iodev_netif_wake, 0); return 0; case IOCTL_MODEM_DUMP_START: @@ -1022,6 +1190,31 @@ static long misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) mif_info("%s: IOCTL_MODEM_DUMP_RESET\n", iod->name); return iod->mc->ops.modem_dump_reset(iod->mc); + case IOCTL_MIF_LOG_DUMP: + iodevs_for_each(iod->msd, iodev_dump_status, 0); + size = MAX_MIF_BUFF_SIZE; + ret = copy_to_user((void __user *)arg, &size, + sizeof(unsigned long)); + if (ret < 0) + return -EFAULT; + + mif_dump_log(iod->mc->msd, iod); + return 0; + + case IOCTL_MIF_DPRAM_DUMP: +#ifdef CONFIG_LINK_DEVICE_DPRAM + if (iod->mc->mdm_data->link_types & LINKTYPE(LINKDEV_DPRAM)) { + size = iod->mc->mdm_data->dpram_ctl->dp_size; + ret = copy_to_user((void __user *)arg, &size, + sizeof(unsigned long)); + if (ret < 0) + return -EFAULT; + mif_dump_dpram(iod); + return 0; + } +#endif + return -EINVAL; + default: /* If you need to handle the ioctl for specific link device, * then assign the link ioctl handler to ld->ioctl @@ -1040,18 +1233,18 @@ static ssize_t misc_write(struct file *filp, const char __user *data, { struct io_device *iod = (struct io_device *)filp->private_data; struct link_device *ld = get_current_link(iod); - struct sipc5_frame_data *frm = &iod->meta_frame; struct sk_buff *skb; - u8 *buff; - unsigned headroom; + int ret; + unsigned headroom = 0; unsigned tailroom = 0; size_t tx_size; - int ret; + struct sipc5_frame_data frm; + struct timespec epoch; if (iod->format <= IPC_RFS && iod->id == 0) return -EINVAL; - headroom = sipc5_build_tx_link_header(frm, iod, ld, count); + headroom = tx_build_link_header(&frm, iod, ld, count); if (ld->aligned) tailroom = sipc5_calc_padding_size(headroom + count); @@ -1066,20 +1259,19 @@ static ssize_t misc_write(struct file *filp, const char __user *data, } /* store IPC link header*/ - buff = skb_put(skb, headroom); - memcpy(buff, frm->hdr, headroom); + memcpy(skb_put(skb, headroom), frm.hdr, headroom); /* store IPC message */ - buff = skb_put(skb, count); - if (copy_from_user(buff, data, count) != 0) { + if (copy_from_user(skb_put(skb, count), data, count) != 0) { if (skb) dev_kfree_skb_any(skb); return -EFAULT; } if (iod->id == SIPC5_CH_ID_FMT_0) { - mif_ipc_log(iod->mc, MIF_IOD_TX_EVT, iod, ld, buff, count); - mif_flush_logs(ld->mc); + getnstimeofday(&epoch); + mif_time_log(iod->mc->msd, epoch, NULL, 0); + mif_ipc_log(MIF_IPC_RL2AP, iod->mc->msd, skb->data, skb->len); } /* store padding */ @@ -1112,6 +1304,7 @@ static ssize_t misc_read(struct file *filp, char *buf, size_t count, struct sk_buff_head *rxq = &iod->sk_rx_q; struct sk_buff *skb; int copied = 0; + struct timespec epoch; skb = skb_dequeue(rxq); if (!skb) { @@ -1120,9 +1313,9 @@ static ssize_t misc_read(struct file *filp, char *buf, size_t count, } if (iod->id == SIPC5_CH_ID_FMT_0) { - mif_ipc_log(iod->mc, MIF_IOD_RX_EVT, iod, NULL, skb->data, - skb->len); - mif_flush_logs(iod->mc); + getnstimeofday(&epoch); + mif_time_log(iod->mc->msd, epoch, NULL, 0); + mif_ipc_log(MIF_IPC_AP2RL, iod->mc->msd, skb->data, skb->len); } copied = skb->len > count ? count : skb->len; @@ -1200,7 +1393,7 @@ static int vnet_open(struct net_device *ndev) { struct vnet *vnet = netdev_priv(ndev); - mif_info("%s\n", vnet->iod->name); + mif_err("%s\n", vnet->iod->name); netif_start_queue(ndev); atomic_inc(&vnet->iod->opened); @@ -1211,10 +1404,11 @@ static int vnet_stop(struct net_device *ndev) { struct vnet *vnet = netdev_priv(ndev); - mif_info("%s\n", vnet->iod->name); + mif_err("%s\n", vnet->iod->name); atomic_dec(&vnet->iod->opened); netif_stop_queue(ndev); + skb_queue_purge(&vnet->iod->sk_rx_q); return 0; } @@ -1223,32 +1417,38 @@ static int vnet_xmit(struct sk_buff *skb, struct net_device *ndev) struct vnet *vnet = netdev_priv(ndev); struct io_device *iod = vnet->iod; struct link_device *ld = get_current_link(iod); - struct sipc5_frame_data *frm = &iod->meta_frame; struct sk_buff *skb_new; - unsigned headroom; - unsigned tailroom = 0; int ret; - -#if 0 - mif_ipc_log(iod->mc, MIF_IOD_TX_EVT, iod, ld, skb->data, skb->len); - mif_flush_logs(ld->mc); -#endif + unsigned headroom = 0; + unsigned tailroom = 0; + unsigned long tx_bytes = skb->len; + struct iphdr *ip_header = NULL; + struct sipc5_frame_data frm; /* When use `handover' with Network Bridge, - * user -> TCP/IP(kernel) -> bridge device -> TCP/IP(kernel) -> this. - * - * We remove the one ethernet header of skb before using skb->len, - * because the skb has two ethernet headers. + * user -> bridge device(rmnet0) -> real rmnet(xxxx_rmnet0) -> here. + * bridge device is ethernet device unlike xxxx_rmnet(net device). + * We remove the an ethernet header of skb before using skb->len, + * because bridge device added an ethernet header to skb. */ if (iod->use_handover) { if (iod->id >= PS_DATA_CH_0 && iod->id <= PS_DATA_CH_LAST) skb_pull(skb, sizeof(struct ethhdr)); } - headroom = sipc5_build_tx_link_header(frm, iod, ld, skb->len); + headroom = tx_build_link_header(&frm, iod, ld, skb->len); + + /* ip loop-back */ + ip_header = (struct iphdr *)skb->data; + if (iod->msd->loopback_ipaddr && + ip_header->daddr == iod->msd->loopback_ipaddr) { + swap(ip_header->saddr, ip_header->daddr); + frm.ch_id = DATA_LOOPBACK_CHANNEL; + frm.hdr[SIPC5_CH_ID_OFFSET] = DATA_LOOPBACK_CHANNEL; + } if (ld->aligned) - tailroom = sipc5_calc_padding_size(frm->len); + tailroom = sipc5_calc_padding_size(frm.len); if (skb_headroom(skb) < headroom || skb_tailroom(skb) < tailroom) { mif_debug("%s: skb_copy_expand needed\n", iod->name); @@ -1263,7 +1463,7 @@ static int vnet_xmit(struct sk_buff *skb, struct net_device *ndev) skb_new = skb; } - memcpy(skb_push(skb_new, headroom), frm->hdr, headroom); + memcpy(skb_push(skb_new, headroom), frm.hdr, headroom); if (tailroom) skb_put(skb_new, tailroom); @@ -1278,7 +1478,7 @@ static int vnet_xmit(struct sk_buff *skb, struct net_device *ndev) } ndev->stats.tx_packets++; - ndev->stats.tx_bytes += skb->len; + ndev->stats.tx_bytes += tx_bytes; return NETDEV_TX_OK; } @@ -1327,13 +1527,13 @@ int sipc5_init_io_device(struct io_device *iod) /* Get data from link device */ mif_debug("%s: SIPC version = %d\n", iod->name, iod->ipc_version); iod->recv = io_dev_recv_data_from_link_dev; + iod->recv_skb = io_dev_recv_skb_from_link_dev; /* Register misc or net device */ switch (iod->io_typ) { case IODEV_MISC: init_waitqueue_head(&iod->wq); skb_queue_head_init(&iod->sk_rx_q); - INIT_DELAYED_WORK(&iod->rx_work, rx_iodev_work); iod->miscdev.minor = MISC_DYNAMIC_MINOR; iod->miscdev.name = iod->name; @@ -1347,8 +1547,6 @@ int sipc5_init_io_device(struct io_device *iod) case IODEV_NET: skb_queue_head_init(&iod->sk_rx_q); - INIT_DELAYED_WORK(&iod->rx_work, rx_iodev_work); - if (iod->use_handover) iod->ndev = alloc_netdev(0, iod->name, vnet_setup_ether); @@ -1375,7 +1573,6 @@ int sipc5_init_io_device(struct io_device *iod) case IODEV_DUMMY: skb_queue_head_init(&iod->sk_rx_q); - /* in sipc5, does not need rx_iodev_work on DUMMY */ iod->miscdev.minor = MISC_DYNAMIC_MINOR; iod->miscdev.name = iod->name; @@ -1389,7 +1586,16 @@ int sipc5_init_io_device(struct io_device *iod) if (ret) mif_info("%s: ERR! device_create_file fail\n", iod->name); - + ret = device_create_file(iod->miscdev.this_device, + &attr_loopback); + if (ret) + mif_err("failed to create `loopback file' : %s\n", + iod->name); + ret = device_create_file(iod->miscdev.this_device, + &attr_txlink); + if (ret) + mif_err("failed to create `txlink file' : %s\n", + iod->name); break; default: diff --git a/drivers/misc/modem_if/sipc5_modem.c b/drivers/misc/modem_if/sipc5_modem.c index a1287b5..f5e33d3 100644 --- a/drivers/misc/modem_if/sipc5_modem.c +++ b/drivers/misc/modem_if/sipc5_modem.c @@ -41,7 +41,41 @@ #define FMT_WAKE_TIME (HZ/2) #define RAW_WAKE_TIME (HZ*6) -static struct modem_ctl *create_modemctl_device(struct platform_device *pdev) +static struct modem_shared *create_modem_shared_data(void) +{ + struct modem_shared *msd; + int size = MAX_MIF_BUFF_SIZE; + + msd = kzalloc(sizeof(struct modem_shared), GFP_KERNEL); + if (!msd) + return NULL; + + /* initialize link device list */ + INIT_LIST_HEAD(&msd->link_dev_list); + + /* initialize tree of io devices */ + msd->iodevs_tree_chan = RB_ROOT; + msd->iodevs_tree_fmt = RB_ROOT; + + msd->storage.cnt = 0; + msd->storage.addr = kzalloc(MAX_MIF_BUFF_SIZE + + (MAX_MIF_SEPA_SIZE * 2), GFP_KERNEL); + if (!msd->storage.addr) { + mif_err("IPC logger buff alloc failed!!\n"); + return NULL; + } + memset(msd->storage.addr, 0, size + (MAX_MIF_SEPA_SIZE * 2)); + memcpy(msd->storage.addr, MIF_SEPARATOR, MAX_MIF_SEPA_SIZE); + msd->storage.addr += MAX_MIF_SEPA_SIZE; + memcpy(msd->storage.addr, &size, MAX_MIF_SEPA_SIZE); + msd->storage.addr += MAX_MIF_SEPA_SIZE; + spin_lock_init(&msd->lock); + + return msd; +} + +static struct modem_ctl *create_modemctl_device(struct platform_device *pdev, + struct modem_shared *msd) { int ret = 0; struct modem_data *pdata; @@ -53,6 +87,7 @@ static struct modem_ctl *create_modemctl_device(struct platform_device *pdev) if (!modemctl) return NULL; + modemctl->msd = msd; modemctl->dev = dev; modemctl->phone_state = STATE_OFFLINE; @@ -60,13 +95,6 @@ static struct modem_ctl *create_modemctl_device(struct platform_device *pdev) modemctl->mdm_data = pdata; modemctl->name = pdata->name; - /* initialize link device list */ - INIT_LIST_HEAD(&modemctl->commons.link_dev_list); - - /* initialize tree of io devices */ - modemctl->commons.iodevs_tree_chan = RB_ROOT; - modemctl->commons.iodevs_tree_fmt = RB_ROOT; - /* init modemctl device for getting modemctl operations */ ret = call_modem_init_func(modemctl, pdata); if (ret) { @@ -74,23 +102,14 @@ static struct modem_ctl *create_modemctl_device(struct platform_device *pdev) return NULL; } - modemctl->use_mif_log = pdata->use_mif_log; - if (pdata->use_mif_log) - mif_err("<%s> IPC logger can be used.\n", pdata->name); - - ret = mif_init_log(modemctl); - if (ret < 0) { - kfree(modemctl); - return NULL; - } - mif_info("%s is created!!!\n", pdata->name); return modemctl; } static struct io_device *create_io_device(struct modem_io_t *io_t, - struct modem_ctl *modemctl, struct modem_data *pdata) + struct modem_shared *msd, struct modem_ctl *modemctl, + struct modem_data *pdata) { int ret = 0; struct io_device *iod = NULL; @@ -123,6 +142,16 @@ static struct io_device *create_io_device(struct modem_io_t *io_t, mif_info("Bood device = %s\n", iod->name); } + /* link between io device and modem shared */ + iod->msd = msd; + + /* add iod to rb_tree */ + if (iod->format != IPC_RAW) + insert_iod_with_format(msd, iod->format, iod); + + if (sipc5_is_not_reserved_channel(iod->id)) + insert_iod_with_channel(msd, iod->id, iod); + /* register misc device or net device */ ret = sipc5_init_io_device(iod); if (ret) { @@ -135,21 +164,14 @@ static struct io_device *create_io_device(struct modem_io_t *io_t, return iod; } -static int attach_devices(struct modem_ctl *mc, struct io_device *iod, - enum modem_link tx_link) +static int attach_devices(struct io_device *iod, enum modem_link tx_link) { + struct modem_shared *msd = iod->msd; struct link_device *ld; unsigned ch; - /* add iod to rb_tree */ - if (iod->format != IPC_RAW) - insert_iod_with_format(&mc->commons, iod->format, iod); - - if (sipc5_is_not_reserved_channel(iod->id)) - insert_iod_with_channel(&mc->commons, iod->id, iod); - /* find link type for this io device */ - list_for_each_entry(ld, &mc->commons.link_dev_list, list) { + list_for_each_entry(ld, &msd->link_dev_list, list) { if (IS_CONNECTED(iod, ld)) { /* The count 1 bits of iod->link_types is count * of link devices of this iod. @@ -210,14 +232,21 @@ static int __devinit modem_probe(struct platform_device *pdev) { int i; struct modem_data *pdata = pdev->dev.platform_data; - struct modem_ctl *modemctl; + struct modem_shared *msd = NULL; + struct modem_ctl *modemctl = NULL; struct io_device *iod[pdata->num_iodevs]; struct link_device *ld; mif_err("%s\n", pdev->name); memset(iod, 0, sizeof(iod)); - modemctl = create_modemctl_device(pdev); + msd = create_modem_shared_data(); + if (!msd) { + mif_err("msd == NULL\n"); + goto err_free_modemctl; + } + + modemctl = create_modemctl_device(pdev, msd); if (!modemctl) { mif_err("modemctl == NULL\n"); goto err_free_modemctl; @@ -235,20 +264,21 @@ static int __devinit modem_probe(struct platform_device *pdev) mif_err("link created: %s\n", ld->name); ld->link_type = i; ld->mc = modemctl; - list_add(&ld->list, &modemctl->commons.link_dev_list); + ld->msd = msd; + list_add(&ld->list, &msd->link_dev_list); } } /* create io deivces and connect to modemctl device */ for (i = 0; i < pdata->num_iodevs; i++) { - iod[i] = create_io_device(&pdata->iodevs[i], modemctl, pdata); + iod[i] = create_io_device(&pdata->iodevs[i], msd, modemctl, + pdata); if (!iod[i]) { mif_err("iod[%d] == NULL\n", i); goto err_free_modemctl; } - attach_devices(modemctl, iod[i], - pdata->iodevs[i].tx_link); + attach_devices(iod[i], pdata->iodevs[i].tx_link); } platform_set_drvdata(pdev, modemctl); @@ -265,6 +295,9 @@ err_free_modemctl: if (modemctl != NULL) kfree(modemctl); + if (msd != NULL) + kfree(msd); + return -ENOMEM; } |