diff options
author | RGIB <gibellini.roberto@gmail.com> | 2016-05-27 15:41:13 +0200 |
---|---|---|
committer | Roberto Gibellini <gibellini.roberto@gmail.com> | 2016-05-27 06:45:25 -0700 |
commit | 462bab3a059ebeeb45dc3ebfef4ee8b39a4757e3 (patch) | |
tree | 7f5dc0a29e59087c4e83fe88173d3ce64a93d4e1 | |
parent | 206f43e8ae09166ba608f520c60bb208661769ca (diff) | |
download | kernel_samsung_smdk4412-462bab3a059ebeeb45dc3ebfef4ee8b39a4757e3.zip kernel_samsung_smdk4412-462bab3a059ebeeb45dc3ebfef4ee8b39a4757e3.tar.gz kernel_samsung_smdk4412-462bab3a059ebeeb45dc3ebfef4ee8b39a4757e3.tar.bz2 |
smdk4412 : modem_if KK driver from N5100ZTCNL4
Change-Id: I7d1e6412700b5db293448aca99c53ac4a52fc1a8
45 files changed, 12437 insertions, 5113 deletions
diff --git a/drivers/misc/modem_if/Kconfig b/drivers/misc/modem_if/Kconfig index 6a6eeab..a425439 100644 --- a/drivers/misc/modem_if/Kconfig +++ b/drivers/misc/modem_if/Kconfig @@ -24,11 +24,21 @@ config CDMA_MODEM_CBP72 depends on SEC_MODEM default n +config CDMA_MODEM_CBP82 + bool "modem chip : VIA CBP8.2" + depends on SEC_MODEM + default n + config LTE_MODEM_CMC221 bool "modem chip : SEC CMC221" depends on SEC_MODEM default n +config UMTS_MODEM_SS222 + bool "modem chip : SEC SS222" + depends on SEC_MODEM + default n + config CDMA_MODEM_MDM6600 bool "modem chip : QC MDM6600" depends on SEC_MODEM @@ -44,6 +54,11 @@ config GSM_MODEM_ESC6270 depends on SEC_MODEM default n +config CDMA_MODEM_QSC6085 + bool "modem chip : Qualcomm QSC6085" + depends on SEC_MODEM + default n + config LINK_DEVICE_MIPI bool "modem driver link device MIPI-HSI" depends on SEC_MODEM @@ -58,6 +73,7 @@ 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 @@ -78,6 +94,11 @@ config LINK_DEVICE_SPI depends on SEC_MODEM default n +config BOOT_DEVICE_SPI + bool "modem driver boot device SPI" + depends on SEC_MODEM + default n + config WORKQUEUE_FRONT bool "IPC: SPI workqueue front" depends on SEC_MODEM diff --git a/drivers/misc/modem_if/Makefile b/drivers/misc/modem_if/Makefile index 5bd62ea..fbb00af 100644 --- a/drivers/misc/modem_if/Makefile +++ b/drivers/misc/modem_if/Makefile @@ -2,7 +2,7 @@ EXTRA_CFLAGS += -Idrivers/misc/modem_if -obj-y += sipc5_modem.o sipc5_io_device.o +obj-y += sipc5_modem.o sipc5_io_device.o sipc5_common.o obj-y += sipc4_modem.o sipc4_io_device.o obj-y += modem_net_flowcontrol_device.o modem_utils.o @@ -10,17 +10,43 @@ obj-$(CONFIG_UMTS_MODEM_XMM6260) += modem_modemctl_device_xmm6260.o obj-$(CONFIG_UMTS_MODEM_XMM6262) += modem_modemctl_device_xmm6262.o obj-$(CONFIG_CDMA_MODEM_CBP71) += modem_modemctl_device_cbp71.o obj-$(CONFIG_CDMA_MODEM_CBP72) += modem_modemctl_device_cbp72.o +obj-$(CONFIG_CDMA_MODEM_CBP82) += modem_modemctl_device_cbp82.o obj-$(CONFIG_LTE_MODEM_CMC221) += modem_modemctl_device_cmc221.o lte_modem_bootloader.o +obj-$(CONFIG_UMTS_MODEM_SS222) += modem_modemctl_device_ss222.o obj-$(CONFIG_CDMA_MODEM_MDM6600) += modem_modemctl_device_mdm6600.o obj-$(CONFIG_GSM_MODEM_ESC6270) += modem_modemctl_device_esc6270.o +obj-$(CONFIG_CDMA_MODEM_QSC6085) += modem_modemctl_device_qsc6085.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 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_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_C2C) += modem_link_device_c2c.o obj-$(CONFIG_LINK_DEVICE_SPI) += modem_link_device_spi.o +obj-$(CONFIG_BOOT_DEVICE_SPI) += modem_boot_device_spi.o + obj-$(CONFIG_SIM_SLOT_SWITCH) += modem_sim_slot_switch.o + +# Check whether or not memory-type interface +ifeq ($(CONFIG_LINK_DEVICE_DPRAM),y) +LINK_DEVICE_MEMORY_INTERFACE=y +endif + +ifeq ($(CONFIG_LINK_DEVICE_PLD),y) +LINK_DEVICE_MEMORY_INTERFACE=y +endif + +ifeq ($(CONFIG_LINK_DEVICE_C2C),y) +LINK_DEVICE_MEMORY_INTERFACE=y +endif + +ifeq ($(CONFIG_LINK_DEVICE_SHMEM),y) +LINK_DEVICE_MEMORY_INTERFACE=y +endif + +ifdef LINK_DEVICE_MEMORY_INTERFACE +obj-y += modem_link_device_memory.o +endif diff --git a/drivers/misc/modem_if/lte_modem_bootloader.c b/drivers/misc/modem_if/lte_modem_bootloader.c index f259aae..0bc8894 100644 --- a/drivers/misc/modem_if/lte_modem_bootloader.c +++ b/drivers/misc/modem_if/lte_modem_bootloader.c @@ -31,7 +31,7 @@ #include <linux/delay.h> #include <linux/spi/spi.h> -#include <linux/platform_data/modem.h> +#include "modem.h" #include <linux/platform_data/lte_modem_bootloader.h> #define LEN_XMIT_DELEY 100 diff --git a/drivers/misc/modem_if/modem.h b/drivers/misc/modem_if/modem.h new file mode 100644 index 0000000..bc4433e --- /dev/null +++ b/drivers/misc/modem_if/modem.h @@ -0,0 +1,435 @@ +/* + * 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_IF_H__ +#define __MODEM_IF_H__ + +#include <linux/platform_device.h> +#include <linux/miscdevice.h> + +enum modem_t { + IMC_XMM6260, + IMC_XMM6262, + VIA_CBP71, + VIA_CBP72, + VIA_CBP82, + SEC_CMC220, + SEC_CMC221, + SEC_SS222, + QC_MDM6600, + QC_ESC6270, + QC_QSC6085, + SPRD_SC8803, + DUMMY, + MAX_MODEM_TYPE +}; + +enum dev_format { + IPC_FMT, + IPC_RAW, + IPC_RFS, + IPC_MULTI_RAW, + IPC_CMD, + IPC_BOOT, + IPC_RAMDUMP, + IPC_DEBUG, + MAX_DEV_FORMAT, +}; +#define MAX_IPC_DEV (IPC_RFS + 1) /* FMT, RAW, RFS */ +#define MAX_SIPC5_DEV (IPC_RAW + 1) /* FMT, RAW */ + +enum modem_io { + IODEV_MISC, + IODEV_NET, + IODEV_DUMMY, +}; + +enum modem_link { + LINKDEV_UNDEFINED, + LINKDEV_MIPI, + LINKDEV_USB, + LINKDEV_HSIC, + LINKDEV_DPRAM, + LINKDEV_PLD, + LINKDEV_C2C, + LINKDEV_SHMEM, + LINKDEV_SPI, + LINKDEV_MAX +}; +#define LINKTYPE(modem_link) (1u << (modem_link)) + +enum modem_network { + UMTS_NETWORK, + CDMA_NETWORK, + TDSCDMA_NETWORK, + LTE_NETWORK, + MAX_MODEM_NETWORK +}; + +enum ap_type { + S5P, + MAX_AP_TYPE +}; + +enum sipc_ver { + NO_SIPC_VER = 0, + SIPC_VER_40 = 40, + SIPC_VER_41 = 41, + SIPC_VER_42 = 42, + SIPC_VER_50 = 50, + MAX_SIPC_VER +}; + +/** + * struct modem_io_t - declaration for io_device + * @name: device name + * @id: for SIPC4, contains format & channel information + * (id & 11100000b)>>5 = format (eg, 0=FMT, 1=RAW, 2=RFS) + * (id & 00011111b) = channel (valid only if format is RAW) + * for SIPC5, contains only 8-bit channel ID + * @format: device format + * @io_type: type of this io_device + * @links: list of link_devices to use this io_device + * for example, if you want to use DPRAM and USB in an io_device. + * .links = LINKTYPE(LINKDEV_DPRAM) | LINKTYPE(LINKDEV_USB) + * @tx_link: when you use 2+ link_devices, set the link for TX. + * If define multiple link_devices in @links, + * you can receive data from them. But, cannot send data to all. + * TX is only one link_device. + * @app: the name of the application that will use this IO device + * + * This structure is used in board-*-modems.c + */ +struct modem_io_t { + char *name; + int id; + enum dev_format format; + enum modem_io io_type; + enum modem_link links; + enum modem_link tx_link; + char *app; +}; + +struct modemlink_pm_data { + char *name; + /* link power contol 2 types : pin & regulator control */ + int (*link_ldo_enable)(bool); + unsigned gpio_link_enable; + unsigned gpio_link_active; + unsigned gpio_link_hostwake; + unsigned gpio_link_slavewake; + int (*link_reconnect)(void); + + /* usb hub only */ + int (*port_enable)(int, int); + int (*hub_standby)(void *); + void *hub_pm_data; + bool has_usbhub; + +#ifdef CONFIG_EXYNOS4_CPUFREQ + /* cpu/bus frequency lock */ + atomic_t freqlock; + atomic_t freq_dpramlock; + int (*freq_lock)(struct device *dev); + int (*freq_unlock)(struct device *dev); + unsigned gpio_cpufreq_lock; +#endif + + int autosuspend_delay_ms; /* if zero, the default value is used */ + void (*ehci_reg_dump)(struct device *); +}; + +struct modemlink_pm_link_activectl { + int gpio_initialized; + int gpio_request_host_active; +}; + +#define RES_DPRAM_MEM_ID 0 +#define RES_DPRAM_SFR_ID 1 + +#define STR_DPRAM_BASE "dpram_base" +#define STR_DPRAM_SFR_BASE "dpram_sfr_base" + +enum dpram_type { + EXT_DPRAM, + AP_IDPRAM, + CP_IDPRAM, + PLD_DPRAM, + MAX_DPRAM_TYPE +}; + +#define DPRAM_SIZE_8KB (8 << 10) +#define DPRAM_SIZE_16KB (16 << 10) +#define DPRAM_SIZE_32KB (32 << 10) +#define DPRAM_SIZE_64KB (64 << 10) +#define DPRAM_SIZE_128KB (128 << 10) + +enum dpram_speed { + DPRAM_SPEED_LOW, + DPRAM_SPEED_MID, + DPRAM_SPEED_HIGH, + MAX_DPRAM_SPEED +}; + +struct dpram_circ { + u16 __iomem *head; + u16 __iomem *tail; + u8 __iomem *buff; + u32 size; +}; + +struct dpram_ipc_device { + char name[16]; + int id; + + struct dpram_circ txq; + struct dpram_circ rxq; + + u16 mask_req_ack; + u16 mask_res_ack; + u16 mask_send; +}; + +struct dpram_ipc_map { + u16 __iomem *magic; + u16 __iomem *access; + + struct dpram_ipc_device dev[MAX_IPC_DEV]; + + u16 __iomem *mbx_cp2ap; + u16 __iomem *mbx_ap2cp; +}; + +struct pld_ipc_map { + u16 __iomem *mbx_ap2cp; + u16 __iomem *magic_ap2cp; + u16 __iomem *access_ap2cp; + + u16 __iomem *mbx_cp2ap; + u16 __iomem *magic_cp2ap; + u16 __iomem *access_cp2ap; + + struct dpram_ipc_device dev[MAX_IPC_DEV]; + + u16 __iomem *address_buffer; +}; + +struct modemlink_dpram_data { + enum dpram_type type; /* DPRAM type */ + enum ap_type ap; /* AP type for AP_IDPRAM */ + + /* Stirct I/O access (e.g. ioread16(), etc.) is required */ + bool strict_io_access; + + /* Aligned access is required */ + int aligned; + + /* Disabled during phone booting */ + bool disabled; + + /* Virtual base address and size */ + u8 __iomem *base; + u32 size; + + /* Pointer to an IPC map (DPRAM or PLD) */ + void *ipc_map; + + /* Timeout of waiting for RES_ACK from CP (in msec) */ + unsigned long res_ack_wait_timeout; + + unsigned boot_size_offset; + unsigned boot_tag_offset; + unsigned boot_count_offset; + unsigned max_boot_frame_size; + + void (*setup_speed)(enum dpram_speed); + void (*clear_int2ap)(void); +}; + +enum shmem_type { + REAL_SHMEM, + C2C_SHMEM, + MAX_SHMEM_TYPE +}; + +#define STR_SHMEM_BASE "shmem_base" + +#define SHMEM_SIZE_1MB (1 << 20) /* 1 MB */ +#define SHMEM_SIZE_2MB (2 << 20) /* 2 MB */ +#define SHMEM_SIZE_4MB (4 << 20) /* 4 MB */ + +/* platform data */ +struct modem_data { + char *name; + + unsigned gpio_cp_on; + unsigned gpio_cp_off; + unsigned gpio_reset_req_n; + unsigned gpio_cp_reset; + + /* for broadcasting AP's PM state (active or sleep) */ + unsigned gpio_pda_active; + + /* for checking aliveness of CP */ + unsigned gpio_phone_active; + int irq_phone_active; + + /* for AP-CP IPC interrupt */ + unsigned gpio_ipc_int2ap; + int irq_ipc_int2ap; + unsigned long irqf_ipc_int2ap; /* IRQ flags */ + unsigned gpio_ipc_int2cp; + + /* for AP-CP power management (PM) handshaking */ + unsigned gpio_ap_wakeup; + int irq_ap_wakeup; + unsigned gpio_ap_status; + unsigned gpio_cp_wakeup; + unsigned gpio_cp_status; + int irq_cp_status; + + /* for USB/HSIC PM */ + unsigned gpio_host_wakeup; + int irq_host_wakeup; + unsigned gpio_host_active; + unsigned gpio_slave_wakeup; + unsigned gpio_hub_suspend; + + unsigned gpio_cp_dump_int; + unsigned gpio_ap_dump_int; + unsigned gpio_flm_uart_sel; + unsigned gpio_cp_warm_reset; +#if defined(CONFIG_MACH_M0_CTC) + unsigned gpio_flm_uart_sel_rev06; +#endif + + unsigned gpio_sim_detect; + int irq_sim_detect; + +#ifdef CONFIG_LINK_DEVICE_PLD + unsigned gpio_fpga1_creset; + unsigned gpio_fpga1_cdone; + unsigned gpio_fpga1_rst_n; + unsigned gpio_fpga1_cs_n; + + unsigned gpio_fpga2_creset; + unsigned gpio_fpga2_cdone; + unsigned gpio_fpga2_rst_n; + unsigned gpio_fpga2_cs_n; +#endif + +#ifdef CONFIG_MACH_U1_KOR_LGT + unsigned gpio_cp_reset_msm; + unsigned gpio_boot_sw_sel; + void (*vbus_on)(void); + void (*vbus_off)(void); + struct regulator *cp_vbus; +#endif + +#ifdef CONFIG_TDSCDMA_MODEM_SPRD8803 + unsigned gpio_ipc_mrdy; + unsigned gpio_ipc_srdy; + unsigned gpio_ipc_sub_mrdy; + unsigned gpio_ipc_sub_srdy; + 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 + + /* Switch with 2 links in a modem */ + unsigned gpio_link_switch; + +#ifdef CONFIG_EXYNOS4_CPUFREQ + /* cpu/bus frequency lock */ + unsigned gpio_cpufreq_lock; +#endif + + /* Modem component */ + enum modem_network modem_net; + enum modem_t modem_type; + enum modem_link link_types; + char *link_name; + + /* Link to DPRAM control functions dependent on each platform */ + struct modemlink_dpram_data *dpram; + + /* SIPC version */ + enum sipc_ver ipc_version; + + /* the number of real IPC devices -> (IPC_RAW + 1) or (IPC_RFS + 1) */ + int max_ipc_dev; + + /* Information of IO devices */ + unsigned num_iodevs; + struct modem_io_t *iodevs; + + /* Modem link PM support */ + struct modemlink_pm_data *link_pm_data; + + /* Handover with 2+ modems */ + bool use_handover; + + /* SIM Detect polarity */ + bool sim_polarity; + + void (*gpio_revers_bias_clear)(void); + void (*gpio_revers_bias_restore)(void); +}; + +#define MODEM_BOOT_DEV_SPI "modem_boot_spi" + +struct modem_boot_spi_platform_data { + const char *name; + unsigned int gpio_cp_status; +}; + +struct modem_boot_spi { + struct miscdevice dev; + struct spi_device *spi_dev; + struct mutex lock; + unsigned gpio_cp_status; +}; +#define to_modem_boot_spi(misc) container_of(misc, struct modem_boot_spi, dev); + +struct utc_time { + u16 year; + u8 mon:4, + day:4; + u8 hour; + u8 min; + u8 sec; + u16 msec; +} __packed; + +extern void get_utc_time(struct utc_time *utc); + +#define LOG_TAG "mif: " +#define CALLER (__builtin_return_address(0)) + +#define mif_err(fmt, ...) \ + pr_err(LOG_TAG "%s: " pr_fmt(fmt), __func__, ##__VA_ARGS__) +#define mif_debug(fmt, ...) \ + pr_debug(LOG_TAG "%s: " pr_fmt(fmt), __func__, ##__VA_ARGS__) +#define mif_info(fmt, ...) \ + pr_info(LOG_TAG "%s: " pr_fmt(fmt), __func__, ##__VA_ARGS__) +#define mif_trace(fmt, ...) \ + printk(KERN_DEBUG "mif: %s: %d: called(%pF): " fmt, \ + __func__, __LINE__, __builtin_return_address(0), ##__VA_ARGS__) + +#endif diff --git a/drivers/misc/modem_if/modem_boot_device_spi.c b/drivers/misc/modem_if/modem_boot_device_spi.c new file mode 100644 index 0000000..369e473 --- /dev/null +++ b/drivers/misc/modem_if/modem_boot_device_spi.c @@ -0,0 +1,279 @@ +/* + * Copyright (C) 2011 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/uaccess.h> +#include <linux/fs.h> +#include <linux/wait.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/mutex.h> +#include <linux/irq.h> +#include <linux/gpio.h> +#include <linux/wakelock.h> +#include <linux/delay.h> +#include <linux/spi/spi.h> + +#include "modem.h" +#include "modem_prj.h" + +#define SPI_XMIT_DELEY 100 + +static int check_cp_status(unsigned int gpio_cp_status, unsigned int count) +{ + int ret = 0; + int cnt = 0; + + while (1) { + if (gpio_get_value(gpio_cp_status) != 0) { + ret = 0; + break; + } + + cnt++; + if (cnt >= count) { + mif_err("ERR! gpio_cp_status == 0 (cnt %d)\n", cnt); + ret = -EFAULT; + break; + } + + msleep(20); + } + + return ret; +} + +static inline int spi_send(struct modem_boot_spi *loader, const char val) +{ + char buff[1]; + int ret; + struct spi_message msg; + struct spi_transfer xfer = { + .len = 1, + .tx_buf = buff, + }; + + buff[0] = val; + + spi_message_init(&msg); + spi_message_add_tail(&xfer, &msg); + + ret = spi_sync(loader->spi_dev, &msg); + if (ret < 0) + mif_err("ERR! spi_sync fail (err %d)\n", ret); + + return ret; +} + +static int spi_boot_write(struct modem_boot_spi *loader, const char *addr, + const long len) +{ + int i; + int ret = 0; + char *buff = NULL; + mif_err("+++\n"); + + buff = kzalloc(len, GFP_KERNEL); + if (!buff) { + mif_err("ERR! kzalloc(%ld) fail\n", len); + ret = -ENOMEM; + goto exit; + } + + ret = copy_from_user(buff, (const void __user *)addr, len); + if (ret) { + mif_err("ERR! copy_from_user fail (err %d)\n", ret); + ret = -EFAULT; + goto exit; + } + + for (i = 0 ; i < len ; i++) { + ret = spi_send(loader, buff[i]); + if (ret < 0) { + mif_err("ERR! spi_send fail (err %d)\n", ret); + goto exit; + } + } + +exit: + if (buff) + kfree(buff); + + mif_err("---\n"); + return ret; +} + +static int spi_boot_open(struct inode *inode, struct file *filp) +{ + struct modem_boot_spi *loader = to_modem_boot_spi(filp->private_data); + filp->private_data = loader; + return 0; +} + +static long spi_boot_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + int ret = 0; + struct modem_firmware img; + struct modem_boot_spi *loader = filp->private_data; + + mutex_lock(&loader->lock); + switch (cmd) { + case IOCTL_MODEM_XMIT_BOOT: + ret = copy_from_user(&img, (const void __user *)arg, + sizeof(struct modem_firmware)); + if (ret) { + mif_err("ERR! copy_from_user fail (err %d)\n", ret); + ret = -EFAULT; + goto exit_err; + } + mif_info("IOCTL_MODEM_XMIT_BOOT (size %d)\n", img.size); + + ret = spi_boot_write(loader, img.binary, img.size); + if (ret < 0) { + mif_err("ERR! spi_boot_write fail (err %d)\n", ret); + break; + } + + if (!loader->gpio_cp_status) + break; + + ret = check_cp_status(loader->gpio_cp_status, 100); + if (ret < 0) + mif_err("ERR! check_cp_status fail (err %d)\n", ret); + + break; + + default: + mif_err("ioctl cmd error\n"); + ret = -ENOIOCTLCMD; + + break; + } + mutex_unlock(&loader->lock); + +exit_err: + return ret; +} + +static const struct file_operations modem_spi_boot_fops = { + .owner = THIS_MODULE, + .open = spi_boot_open, + .unlocked_ioctl = spi_boot_ioctl, +}; + +static int __devinit modem_spi_boot_probe(struct spi_device *spi) +{ + int ret; + struct modem_boot_spi *loader; + struct modem_boot_spi_platform_data *pdata; + mif_debug("+++\n"); + + loader = kzalloc(sizeof(*loader), GFP_KERNEL); + if (!loader) { + mif_err("failed to allocate for modem_boot_spi\n"); + ret = -ENOMEM; + goto err_alloc; + } + mutex_init(&loader->lock); + + spi->bits_per_word = 8; + + if (spi_setup(spi)) { + mif_err("ERR! spi_setup fail\n"); + ret = -EINVAL; + goto err_setup; + } + loader->spi_dev = spi; + + if (!spi->dev.platform_data) { + mif_err("ERR! no platform_data\n"); + ret = -EINVAL; + goto err_setup; + } + pdata = (struct modem_boot_spi_platform_data *)spi->dev.platform_data; + + loader->gpio_cp_status = pdata->gpio_cp_status; + + spi_set_drvdata(spi, loader); + + loader->dev.minor = MISC_DYNAMIC_MINOR; + loader->dev.name = MODEM_BOOT_DEV_SPI; + loader->dev.fops = &modem_spi_boot_fops; + ret = misc_register(&loader->dev); + if (ret) { + mif_err("ERR! misc_register fail (err %d)\n", ret); + goto err_setup; + } + + mif_debug("---\n"); + return 0; + +err_setup: + mutex_destroy(&loader->lock); + kfree(loader); + +err_alloc: + mif_err("xxx\n"); + return ret; +} + +static int __devexit modem_spi_boot_remove(struct spi_device *spi) +{ + struct modem_boot_spi *loader = spi_get_drvdata(spi); + + misc_deregister(&loader->dev); + mutex_destroy(&loader->lock); + kfree(loader); + + return 0; +} + +static struct spi_driver modem_boot_device_spi_driver = { + .driver = { + .name = MODEM_BOOT_DEV_SPI, + .owner = THIS_MODULE, + }, + .probe = modem_spi_boot_probe, + .remove = __devexit_p(modem_spi_boot_remove), +}; + +static int __init modem_boot_device_spi_init(void) +{ + int err; + + err = spi_register_driver(&modem_boot_device_spi_driver); + if (err) { + mif_err("spi_register_driver fail (err %d)\n", err); + return err; + } + + return 0; +} + +static void __exit modem_boot_device_spi_exit(void) +{ + spi_unregister_driver(&modem_boot_device_spi_driver); +} + +module_init(modem_boot_device_spi_init); +module_exit(modem_boot_device_spi_exit); + +MODULE_DESCRIPTION("SPI Driver for Downloading Modem Bootloader"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/misc/modem_if/modem_link_device_c2c.c b/drivers/misc/modem_if/modem_link_device_c2c.c index acbaadf..b51cf9e 100644 --- a/drivers/misc/modem_if/modem_link_device_c2c.c +++ b/drivers/misc/modem_if/modem_link_device_c2c.c @@ -1,6 +1,4 @@ -/* /linux/drivers/new_modem_if/link_dev_c2c.c - * - * Copyright (C) 2010 Google, Inc. +/* * Copyright (C) 2010 Samsung Electronics. * * This software is licensed under the terms of the GNU General Public @@ -14,9 +12,9 @@ * */ -#include <linux/init.h> #include <linux/irq.h> #include <linux/gpio.h> +#include <linux/time.h> #include <linux/interrupt.h> #include <linux/timer.h> #include <linux/wakelock.h> @@ -24,38 +22,2279 @@ #include <linux/wait.h> #include <linux/sched.h> #include <linux/vmalloc.h> -#include <linux/proc_fs.h> #include <linux/if_arp.h> #include <linux/platform_device.h> +#include <linux/kallsyms.h> +#include <linux/suspend.h> +#ifdef CONFIG_FB +#include <linux/fb.h> +#endif +#include <plat/gpio-cfg.h> +#include <mach/gpio.h> +#include <mach/regs-gpio.h> +#include <linux/notifier.h> -#include <linux/platform_data/modem.h> -#include <linux/platform_data/c2c.h> +#include "modem.h" #include "modem_prj.h" +#include "modem_utils.h" #include "modem_link_device_c2c.h" +static void trigger_forced_cp_crash(struct shmem_link_device *shmd); + +#ifdef DEBUG_MODEM_IF +static void save_mem_dump(struct shmem_link_device *shmd) +{ + struct link_device *ld = &shmd->ld; + char *path = shmd->dump_path; + struct file *fp; + struct utc_time t; + + get_utc_time(&t); + snprintf(path, MIF_MAX_PATH_LEN, "%s/%s_%d%02d%02d_%02d%02d%02d.dump", + MIF_LOG_DIR, ld->name, t.year, t.mon, t.day, t.hour, t.min, + t.sec); + + fp = mif_open_file(path); + if (!fp) { + mif_err("%s: ERR! %s open fail\n", ld->name, path); + return; + } + mif_err("%s: %s opened\n", ld->name, path); + + mif_save_file(fp, shmd->base, shmd->size); + + mif_close_file(fp); +} + +/** + * mem_dump_work + * @ws: pointer to an instance of work_struct structure + * + * Performs actual file operation for saving a DPRAM dump. + */ +static void mem_dump_work(struct work_struct *ws) +{ + struct shmem_link_device *shmd; + + shmd = container_of(ws, struct shmem_link_device, dump_dwork.work); + if (!shmd) { + mif_err("ERR! no shmd\n"); + return; + } + + save_mem_dump(shmd); +} +#endif + +static void print_pm_status(struct shmem_link_device *shmd, int level) +{ +#ifdef DEBUG_MODEM_IF + struct utc_time t; + u32 magic; + int ap_wakeup; + int ap_status; + int cp_wakeup; + int cp_status; + + if (level < 0) + return; + + get_utc_time(&t); + magic = get_magic(shmd); + ap_wakeup = gpio_get_value(shmd->gpio_ap_wakeup); + ap_status = gpio_get_value(shmd->gpio_ap_status); + cp_wakeup = gpio_get_value(shmd->gpio_cp_wakeup); + cp_status = gpio_get_value(shmd->gpio_cp_status); + + /* + ** PM {ap_wakeup:cp_wakeup:cp_status:ap_status:magic:state} CALLER + */ + if (level > 0) { + pr_err(LOG_TAG "[%02d:%02d:%02d.%03d] C2C PM {%d:%d:%d:%d:%X} " + "%pf\n", t.hour, t.min, t.sec, t.msec, ap_wakeup, + cp_wakeup, cp_status, ap_status, magic, CALLER); + } else { + pr_info(LOG_TAG "[%02d:%02d:%02d.%03d] C2C PM {%d:%d:%d:%d:%X} " + "%pf\n", t.hour, t.min, t.sec, t.msec, ap_wakeup, + cp_wakeup, cp_status, ap_status, magic, CALLER); + } +#endif +} + +static inline void s5p_change_irq_type(int irq, int value) +{ + irq_set_irq_type(irq, value ? IRQ_TYPE_LEVEL_LOW : IRQ_TYPE_LEVEL_HIGH); + /** + * Exynos BSP has a problem when using level-triggering interrupt. + * If the irq type is changed in an interrupt handler, the handler will + * be called again. + * Below is a temporary solution until SYS.LSI resolves this problem. + */ + __raw_writel(eint_irq_to_bit(irq), S5P_EINT_PEND(EINT_REG_NR(irq))); +} + +/** + * recv_int2ap + * @shmd: pointer to an instance of shmem_link_device structure + * + * Returns the value of the CP-to-AP interrupt register. + */ +static inline u16 recv_int2ap(struct shmem_link_device *shmd) +{ + if (shmd->type == C2C_SHMEM) + return (u16)c2c_read_interrupt(); + + if (shmd->mbx2ap) + return *shmd->mbx2ap; + + return 0; +} + +/** + * send_int2cp + * @shmd: pointer to an instance of shmem_link_device structure + * @mask: value to be written to the AP-to-CP interrupt register + */ +static inline void send_int2cp(struct shmem_link_device *shmd, u16 mask) +{ + struct link_device *ld = &shmd->ld; + + if (ld->mode != LINK_MODE_IPC) + mif_info("%s: <by %pf> mask = 0x%04X\n", ld->name, CALLER, mask); + + if (shmd->type == C2C_SHMEM) + c2c_send_interrupt(mask); + + if (shmd->mbx2cp) + *shmd->mbx2cp = mask; +} + +/** + * get_shmem_status + * @shmd: pointer to an instance of shmem_link_device structure + * @dir: direction of communication (TX or RX) + * @mst: pointer to an instance of mem_status structure + * + * Takes a snapshot of the current status of a SHMEM. + */ +static void get_shmem_status(struct shmem_link_device *shmd, + enum circ_dir_type dir, struct mem_status *mst) +{ +#ifdef DEBUG_MODEM_IF + getnstimeofday(&mst->ts); +#endif + + mst->dir = dir; + mst->magic = get_magic(shmd); + mst->access = get_access(shmd); + mst->head[IPC_FMT][TX] = get_txq_head(shmd, IPC_FMT); + mst->tail[IPC_FMT][TX] = get_txq_tail(shmd, IPC_FMT); + mst->head[IPC_FMT][RX] = get_rxq_head(shmd, IPC_FMT); + mst->tail[IPC_FMT][RX] = get_rxq_tail(shmd, IPC_FMT); + mst->head[IPC_RAW][TX] = get_txq_head(shmd, IPC_RAW); + mst->tail[IPC_RAW][TX] = get_txq_tail(shmd, IPC_RAW); + mst->head[IPC_RAW][RX] = get_rxq_head(shmd, IPC_RAW); + mst->tail[IPC_RAW][RX] = get_rxq_tail(shmd, IPC_RAW); +} + +static inline int check_link_status(struct shmem_link_device *shmd) +{ + u32 magic = get_magic(shmd); + int cnt; + + if (gpio_get_value(shmd->gpio_cp_status) != 0 && magic == SHM_IPC_MAGIC) + return 0; + + cnt = 0; + while (gpio_get_value(shmd->gpio_cp_status) == 0) { + if (gpio_get_value(shmd->gpio_ap_status) == 0) { + print_pm_status(shmd, 1); + gpio_set_value(shmd->gpio_ap_status, 1); + } + + cnt++; + if (cnt >= 100) { + mif_err("ERR! cp_status != 1 (cnt %d)\n", cnt); + return -EACCES; + } + + if (in_interrupt()) + udelay(100); + else + usleep_range(100, 200); + } + + cnt = 0; + while (1) { + magic = get_magic(shmd); + if (magic == SHM_IPC_MAGIC) + break; + + cnt++; + if (cnt >= 100) { + mif_err("ERR! magic 0x%X != SHM_IPC_MAGIC (cnt %d)\n", + magic, cnt); + return -EACCES; + } + + if (in_interrupt()) + udelay(100); + else + usleep_range(100, 200); + } + + return 0; +} + +static void release_cp_wakeup(struct work_struct *ws) +{ + struct shmem_link_device *shmd; + struct link_device *ld; + int i; + unsigned long flags; + + shmd = container_of(ws, struct shmem_link_device, cp_sleep_dwork.work); + + spin_lock_irqsave(&shmd->pm_lock, flags); + i = atomic_read(&shmd->ref_cnt); + spin_unlock_irqrestore(&shmd->pm_lock, flags); + if (i > 0) + goto reschedule; + + /* + * If there is any IPC message remained in a TXQ, AP must prevent CP + * from going to sleep. + */ + ld = &shmd->ld; + for (i = 0; i < ld->max_ipc_dev; i++) { + if (ld->skb_txq[i]->qlen > 0) + goto reschedule; + } + + if (gpio_get_value(shmd->gpio_ap_wakeup)) + goto reschedule; + + gpio_set_value(shmd->gpio_cp_wakeup, 0); + print_pm_status(shmd, 1); + return; + +reschedule: + if (!work_pending(&shmd->cp_sleep_dwork.work)) { + queue_delayed_work(system_nrt_wq, &shmd->cp_sleep_dwork, + msecs_to_jiffies(CP_WAKEUP_HOLD_TIME)); + } +} + +static void release_ap_status(struct work_struct *ws) +{ + struct shmem_link_device *shmd; + struct link_device *ld; + int i; + unsigned long flags; + + shmd = container_of(ws, struct shmem_link_device, link_off_dwork.work); + + spin_lock_irqsave(&shmd->pm_lock, flags); + i = atomic_read(&shmd->ref_cnt); + spin_unlock_irqrestore(&shmd->pm_lock, flags); + if (i > 0) + goto reschedule; + + if (gpio_get_value(shmd->gpio_cp_status)) + goto reschedule; + + gpio_set_value(shmd->gpio_ap_status, 0); + print_pm_status(shmd, 1); + + if (wake_lock_active(&shmd->cp_wlock)) + wake_unlock(&shmd->cp_wlock); + + return; + +reschedule: + if (!work_pending(&shmd->link_off_dwork.work)) { + queue_delayed_work(system_nrt_wq, &shmd->link_off_dwork, + msecs_to_jiffies(100)); + } +} + +/** + * forbid_cp_sleep + * @shmd: pointer to an instance of shmem_link_device structure + * + * Wakes up a CP if it can sleep and increases the "ref_cnt" counter in the + * shmem_link_device instance. + * + * CAUTION!!! permit_cp_sleep() MUST be invoked after forbid_cp_sleep() success + * to decrease the "ref_cnt" counter. + */ +static int forbid_cp_sleep(struct shmem_link_device *shmd) +{ + struct link_device *ld = &shmd->ld; + int err = 0; + unsigned long flags; + + spin_lock_irqsave(&shmd->pm_lock, flags); + atomic_inc(&shmd->ref_cnt); + if (gpio_get_value(shmd->gpio_ap_status) == 0) { + gpio_set_value(shmd->gpio_ap_status, 1); + print_pm_status(shmd, 1); + } + spin_unlock_irqrestore(&shmd->pm_lock, flags); + + if (work_pending(&shmd->cp_sleep_dwork.work)) + cancel_delayed_work(&shmd->cp_sleep_dwork); + + if (work_pending(&shmd->link_off_dwork.work)) + cancel_delayed_work(&shmd->link_off_dwork); + + if (gpio_get_value(shmd->gpio_cp_wakeup) == 0) { + gpio_set_value(shmd->gpio_cp_wakeup, 1); + print_pm_status(shmd, 1); + } + + if (check_link_status(shmd) < 0) { + print_pm_status(shmd, 1); + mif_err("%s: ERR! check_link_status fail\n", ld->name); + err = -EACCES; + goto exit; + } + +exit: + return err; +} + +/** + * permit_cp_sleep + * @shmd: pointer to an instance of shmem_link_device structure + * + * Decreases the "ref_cnt" counter in the shmem_link_device instance if it can + * sleep and allows a CP to sleep only if the value of "ref_cnt" counter is + * less than or equal to 0. + * + * MUST be invoked after forbid_cp_sleep() success to decrease the "ref_cnt" + * counter. + */ +static void permit_cp_sleep(struct shmem_link_device *shmd) +{ + struct link_device *ld = &shmd->ld; + unsigned long flags; + + spin_lock_irqsave(&shmd->pm_lock, flags); + + if (atomic_dec_return(&shmd->ref_cnt) > 0) { + spin_unlock_irqrestore(&shmd->pm_lock, flags); + return; + } + + atomic_set(&shmd->ref_cnt, 0); + spin_unlock_irqrestore(&shmd->pm_lock, flags); + + /* Hold gpio_cp_wakeup for CP_WAKEUP_HOLD_TIME until CP finishes RX */ + if (!work_pending(&shmd->cp_sleep_dwork.work)) { + queue_delayed_work(system_nrt_wq, &shmd->cp_sleep_dwork, + msecs_to_jiffies(CP_WAKEUP_HOLD_TIME)); + } +} + +/** + * ap_wakeup_handler: interrupt handler for a wakeup interrupt + * @irq: IRQ number + * @data: pointer to a data + * + * 1) Reads the interrupt value + * 2) Performs interrupt handling + */ +static irqreturn_t ap_wakeup_handler(int irq, void *data) +{ + struct shmem_link_device *shmd = (struct shmem_link_device *)data; + struct link_device *ld = (struct link_device *)&shmd->ld; + int ap_wakeup = gpio_get_value(shmd->gpio_ap_wakeup); + int ap_status = gpio_get_value(shmd->gpio_ap_status); + + s5p_change_irq_type(irq, ap_wakeup); + + if (ld->mode != LINK_MODE_IPC) + goto exit; + + if (work_pending(&shmd->cp_sleep_dwork.work)) + __cancel_delayed_work(&shmd->cp_sleep_dwork); + + print_pm_status(shmd, 1); + + if (ap_wakeup) { + if (work_pending(&shmd->link_off_dwork.work)) + __cancel_delayed_work(&shmd->link_off_dwork); + + if (!wake_lock_active(&shmd->ap_wlock)) + wake_lock(&shmd->ap_wlock); + + if (!c2c_suspended() && !ap_status) + gpio_set_value(shmd->gpio_ap_status, 1); + } else { + if (wake_lock_active(&shmd->ap_wlock)) + wake_unlock(&shmd->ap_wlock); + + queue_delayed_work(system_nrt_wq, &shmd->cp_sleep_dwork, + msecs_to_jiffies(CP_WAKEUP_HOLD_TIME)); + } + +exit: + return IRQ_HANDLED; +} + +static irqreturn_t cp_status_handler(int irq, void *data) +{ + struct shmem_link_device *shmd = (struct shmem_link_device *)data; + struct link_device *ld = (struct link_device *)&shmd->ld; + int cp_status = gpio_get_value(shmd->gpio_cp_status); + unsigned long flags; + + spin_lock_irqsave(&shmd->pm_lock, flags); + + s5p_change_irq_type(irq, cp_status); + + if (ld->mode != LINK_MODE_IPC) + goto exit; + + if (work_pending(&shmd->link_off_dwork.work)) + __cancel_delayed_work(&shmd->link_off_dwork); + + print_pm_status(shmd, 1); + + if (cp_status) { + if (!wake_lock_active(&shmd->cp_wlock)) + wake_lock(&shmd->cp_wlock); + } else { + if (atomic_read(&shmd->ref_cnt) > 0) { + queue_delayed_work(system_nrt_wq, &shmd->link_off_dwork, + msecs_to_jiffies(10)); + } else { + gpio_set_value(shmd->gpio_ap_status, 0); + if (wake_lock_active(&shmd->cp_wlock)) + wake_unlock(&shmd->cp_wlock); + } + } + +exit: + spin_unlock_irqrestore(&shmd->pm_lock, flags); + return IRQ_HANDLED; +} + +#if 1 +/* Functions for IPC/BOOT/DUMP RX */ +#endif + +/** + * handle_cp_crash + * @shmd: pointer to an instance of shmem_link_device structure + * + * Actual handler for the CRASH_EXIT command from a CP. + */ +static void handle_cp_crash(struct shmem_link_device *shmd) +{ + struct link_device *ld = &shmd->ld; + struct io_device *iod; + int i; + + if (shmd->forced_cp_crash) + shmd->forced_cp_crash = false; + + /* Stop network interfaces */ + mif_netif_stop(ld); + + /* Purge the skb_txq in every IPC device (IPC_FMT, IPC_RAW, etc.) */ + for (i = 0; i < MAX_SIPC5_DEV; i++) + skb_queue_purge(ld->skb_txq[i]); + + /* Change the modem state to STATE_CRASH_EXIT for the FMT IO device */ + iod = link_get_iod_with_format(ld, IPC_FMT); + if (iod) + iod->modem_state_changed(iod, STATE_CRASH_EXIT); + + /* time margin for taking state changes by rild */ + mdelay(100); + + /* Change the modem state to STATE_CRASH_EXIT for the BOOT IO device */ + iod = link_get_iod_with_format(ld, IPC_BOOT); + if (iod) + iod->modem_state_changed(iod, STATE_CRASH_EXIT); +} + +/** + * handle_no_cp_crash_ack + * @arg: pointer to an instance of shmem_link_device structure + * + * Invokes handle_cp_crash() to enter the CRASH_EXIT state if there was no + * CRASH_ACK from a CP in FORCE_CRASH_ACK_TIMEOUT. + */ +static void handle_no_cp_crash_ack(unsigned long arg) +{ + struct shmem_link_device *shmd = (struct shmem_link_device *)arg; + struct link_device *ld = &shmd->ld; + + mif_err("%s: ERR! No CRASH_EXIT ACK from CP\n", ld->name); + + handle_cp_crash(shmd); +} + +/** + * trigger_forced_cp_crash + * @shmd: pointer to an instance of shmem_link_device structure + * + * Triggers an enforced CP crash. + */ +static void trigger_forced_cp_crash(struct shmem_link_device *shmd) +{ + struct link_device *ld = &shmd->ld; + struct utc_time t; + + if (ld->mode == LINK_MODE_ULOAD) { + mif_err("%s: <by %pf> ALREADY in progress\n", ld->name, CALLER); + return; + } + ld->mode = LINK_MODE_ULOAD; + shmd->forced_cp_crash = true; + + get_utc_time(&t); + mif_err("%s: [%02d:%02d:%02d.%03d] <by %pf>\n", + ld->name, t.hour, t.min, t.sec, t.msec, CALLER); + + if (!wake_lock_active(&shmd->wlock)) + wake_lock(&shmd->wlock); + +#ifdef DEBUG_MODEM_IF + if (in_interrupt()) + queue_delayed_work(system_nrt_wq, &shmd->dump_dwork, 0); + else + save_mem_dump(shmd); +#endif + + /* Send CRASH_EXIT command to a CP */ + send_int2cp(shmd, INT_CMD(INT_CMD_CRASH_EXIT)); + get_shmem_status(shmd, TX, msq_get_free_slot(&shmd->stat_list)); + + /* If there is no CRASH_ACK from a CP in FORCE_CRASH_ACK_TIMEOUT, + handle_no_cp_crash_ack() will be executed. */ + mif_add_timer(&shmd->crash_ack_timer, FORCE_CRASH_ACK_TIMEOUT, + handle_no_cp_crash_ack, (unsigned long)shmd); + + return; +} + +/** + * cmd_crash_reset_handler + * @shmd: pointer to an instance of shmem_link_device structure + * + * Handles the CRASH_RESET command from a CP. + */ +static void cmd_crash_reset_handler(struct shmem_link_device *shmd) +{ + struct link_device *ld = &shmd->ld; + struct io_device *iod = NULL; + int i; + struct utc_time t; + + ld->mode = LINK_MODE_ULOAD; + + if (!wake_lock_active(&shmd->wlock)) + wake_lock(&shmd->wlock); + + get_utc_time(&t); + mif_err("%s: ERR! [%02d:%02d:%02d.%03d] Recv 0xC7 (CRASH_RESET)\n", + ld->name, t.hour, t.min, t.sec, t.msec); +#ifdef DEBUG_MODEM_IF + queue_delayed_work(system_nrt_wq, &shmd->dump_dwork, 0); +#endif + + /* Stop network interfaces */ + mif_netif_stop(ld); + + /* Purge the skb_txq in every IPC device (IPC_FMT, IPC_RAW, etc.) */ + for (i = 0; i < MAX_SIPC5_DEV; i++) + skb_queue_purge(ld->skb_txq[i]); + + /* Change the modem state to STATE_CRASH_RESET for the FMT IO device */ + iod = link_get_iod_with_format(ld, IPC_FMT); + if (iod) + iod->modem_state_changed(iod, STATE_CRASH_RESET); + + /* time margin for taking state changes by rild */ + mdelay(100); + + /* Change the modem state to STATE_CRASH_RESET for the BOOT IO device */ + iod = link_get_iod_with_format(ld, IPC_BOOT); + if (iod) + iod->modem_state_changed(iod, STATE_CRASH_RESET); +} + +/** + * cmd_crash_exit_handler + * @shmd: pointer to an instance of shmem_link_device structure + * + * Handles the CRASH_EXIT command from a CP. + */ +static void cmd_crash_exit_handler(struct shmem_link_device *shmd) +{ + struct link_device *ld = &shmd->ld; + struct utc_time t; + + ld->mode = LINK_MODE_ULOAD; + + del_timer(&shmd->crash_ack_timer); + + if (!wake_lock_active(&shmd->wlock)) + wake_lock(&shmd->wlock); + + get_utc_time(&t); + mif_err("%s: ERR! [%02d:%02d:%02d.%03d] Recv 0xC9 (CRASH_EXIT)\n", + ld->name, t.hour, t.min, t.sec, t.msec); +#ifdef DEBUG_MODEM_IF + queue_delayed_work(system_nrt_wq, &shmd->dump_dwork, 0); +#endif + + handle_cp_crash(shmd); +} + +/** + * cmd_phone_start_handler + * @shmd: pointer to an instance of shmem_link_device structure + * + * Handles the PHONE_START command from a CP. + */ +static void cmd_phone_start_handler(struct shmem_link_device *shmd) +{ + int err; + struct link_device *ld = &shmd->ld; + struct io_device *iod; + int ap_wakeup = gpio_get_value(shmd->gpio_ap_wakeup); + int cp_status = gpio_get_value(shmd->gpio_cp_status); + + mif_err("%s: Recv 0xC8 (CP_START)\n", ld->name); + + iod = link_get_iod_with_format(ld, IPC_FMT); + if (!iod) { + mif_err("%s: ERR! no iod\n", ld->name); + return; + } + + err = init_shmem_ipc(shmd); + if (err) + return; + + if (wake_lock_active(&shmd->wlock)) + wake_unlock(&shmd->wlock); + + s5p_change_irq_type(shmd->irq_ap_wakeup, ap_wakeup); + if (ap_wakeup && !wake_lock_active(&shmd->ap_wlock)) + wake_lock(&shmd->ap_wlock); + + s5p_change_irq_type(shmd->irq_cp_status, cp_status); + if (cp_status && !wake_lock_active(&shmd->ap_wlock)) + wake_lock(&shmd->cp_wlock); + + ld->mode = LINK_MODE_IPC; + iod->modem_state_changed(iod, STATE_ONLINE); +} + +/** + * cmd_handler: processes a SHMEM command from a CP + * @shmd: pointer to an instance of shmem_link_device structure + * @cmd: SHMEM command from a CP + */ +static void cmd_handler(struct shmem_link_device *shmd, u16 cmd) +{ + struct link_device *ld = &shmd->ld; + + switch (INT_CMD_MASK(cmd)) { + case INT_CMD_CRASH_RESET: + cmd_crash_reset_handler(shmd); + break; + + case INT_CMD_CRASH_EXIT: + cmd_crash_exit_handler(shmd); + break; + + case INT_CMD_PHONE_START: + cmd_phone_start_handler(shmd); + complete_all(&ld->init_cmpl); + break; + + default: + mif_err("%s: unknown command 0x%04X\n", ld->name, cmd); + break; + } +} + +/** + * ipc_rx_work + * @ws: pointer to an instance of work_struct structure + * + * Invokes the recv method in the io_device instance to perform receiving IPC + * messages from each skb. + */ +static void ipc_rx_work(struct work_struct *ws) +{ + struct shmem_link_device *shmd; + struct link_device *ld; + struct io_device *iod; + struct sk_buff *skb; + int i; + + shmd = container_of(ws, struct shmem_link_device, ipc_rx_dwork.work); + ld = &shmd->ld; + + for (i = 0; i < MAX_SIPC5_DEV; i++) { + iod = shmd->iod[i]; + while (1) { + skb = skb_dequeue(ld->skb_rxq[i]); + if (!skb) + break; + iod->recv_skb(iod, ld, skb); + } + } +} + +/** + * rx_ipc_frames + * @shmd: pointer to an instance of shmem_link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * @mst: pointer to an instance of mem_status structure + * + * Returns + * ret < 0 : error + * ret == 0 : ILLEGAL status + * ret > 0 : valid data + * + * Must be invoked only when there is data in the corresponding RXQ. + * + * Requires a recv_skb method in the io_device instance, so this function must + * be used for only SIPC5. + */ +static int rx_ipc_frames(struct shmem_link_device *shmd, int dev, + struct circ_status *circ) +{ + struct link_device *ld = &shmd->ld; + struct sk_buff_head *rxq = ld->skb_rxq[dev]; + struct sk_buff *skb; + int ret; + /** + * variables for the status of the circular queue + */ + u8 *src; + u8 hdr[SIPC5_MIN_HEADER_SIZE]; + /** + * variables for RX processing + */ + int qsize; /* size of the queue */ + int rcvd; /* size of data in the RXQ or error */ + int rest; /* size of the rest data */ + int out; /* index to the start of current frame */ + u8 *dst; /* pointer to the destination buffer */ + int tot; /* total length including padding data */ + + src = circ->buff; + qsize = circ->qsize; + out = circ->out; + rcvd = circ->size; + + rest = rcvd; + tot = 0; + while (rest > 0) { + /* Copy the header in the frame to the header buffer */ + circ_read(hdr, src, qsize, out, SIPC5_MIN_HEADER_SIZE); + + /* Check the config field in the header */ + if (unlikely(!sipc5_start_valid(hdr))) { + mif_err("%s: ERR! %s INVALID config 0x%02X " + "(rcvd %d, rest %d)\n", ld->name, + get_dev_name(dev), hdr[0], rcvd, rest); + ret = -EBADMSG; + goto exit; + } + + /* Verify the total length of the frame (data + padding) */ + tot = sipc5_get_total_len(hdr); + if (unlikely(tot > rest)) { + mif_err("%s: ERR! %s tot %d > rest %d (rcvd %d)\n", + ld->name, get_dev_name(dev), tot, rest, rcvd); + ret = -EBADMSG; + goto exit; + } + + /* 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)); + ret = -ENOMEM; + goto exit; + } + + /* Set the attribute of the skb as "single frame" */ + skbpriv(skb)->single_frame = true; + + /* Read the frame from the RXQ */ + dst = skb_put(skb, tot); + circ_read(dst, src, qsize, out, tot); + + /* Store the skb to the corresponding skb_rxq */ + skb_queue_tail(rxq, skb); + + /* Calculate new out value */ + rest -= tot; + out += tot; + if (unlikely(out >= qsize)) + out -= qsize; + } + + /* Update tail (out) pointer to empty out the RXQ */ + set_rxq_tail(shmd, dev, circ->in); + + return rcvd; + +exit: +#ifdef DEBUG_MODEM_IF + mif_err("%s: ERR! rcvd:%d tot:%d rest:%d\n", ld->name, rcvd, tot, rest); + pr_ipc(1, "c2c: ERR! CP2MIF", (src + out), (rest > 20) ? 20 : rest); +#endif + + return ret; +} + +/** + * msg_handler: receives IPC messages from every RXQ + * @shmd: pointer to an instance of shmem_link_device structure + * @mst: pointer to an instance of mem_status structure + * + * 1) Receives all IPC message frames currently in every IPC RXQ. + * 2) Sends RES_ACK responses if there are REQ_ACK requests from a CP. + * 3) Completes all threads waiting for the corresponding RES_ACK from a CP if + * there is any RES_ACK response. + */ +static void msg_handler(struct shmem_link_device *shmd, struct mem_status *mst) +{ + struct link_device *ld = &shmd->ld; + struct circ_status circ; + int i = 0; + int ret = 0; + + if (!ipc_active(shmd)) + return; + + /* Read data from every RXQ */ + for (i = 0; i < MAX_SIPC5_DEV; i++) { + /* Invoke an RX function only when there is data in the RXQ */ + if (likely(mst->head[i][RX] != mst->tail[i][RX])) { + ret = get_rxq_rcvd(shmd, i, mst, &circ); + if (unlikely(ret < 0)) { + mif_err("%s: ERR! get_rxq_rcvd fail (err %d)\n", + ld->name, ret); +#ifdef DEBUG_MODEM_IF + trigger_forced_cp_crash(shmd); +#endif + return; + } + + ret = rx_ipc_frames(shmd, i, &circ); + if (ret < 0) { +#ifdef DEBUG_MODEM_IF + trigger_forced_cp_crash(shmd); +#endif + reset_rxq_circ(shmd, i); + } + } + } + + /* Schedule soft IRQ for RX */ + queue_delayed_work(system_nrt_wq, &shmd->ipc_rx_dwork, 0); +} + +static void msg_rx_task(unsigned long data) +{ + struct shmem_link_device *shmd = (struct shmem_link_device *)data; + struct link_device *ld = &shmd->ld; + struct mem_status mst; + u16 mask = 0; + + get_shmem_status(shmd, RX, &mst); + + if ((mst.head[IPC_FMT][RX] != mst.tail[IPC_FMT][RX]) + || (mst.head[IPC_RAW][RX] != mst.tail[IPC_RAW][RX])) { +#if 0 + print_mem_status(ld, &mst); +#endif + msg_handler(shmd, &mst); + } + + /* + ** Check and process REQ_ACK (at this time, in == out) + */ + if (unlikely(shmd->dev[IPC_FMT]->req_ack_rcvd)) { + mask |= get_mask_res_ack(shmd, IPC_FMT); + shmd->dev[IPC_FMT]->req_ack_rcvd = 0; + } + + if (unlikely(shmd->dev[IPC_RAW]->req_ack_rcvd)) { + mask |= get_mask_res_ack(shmd, IPC_RAW); + shmd->dev[IPC_RAW]->req_ack_rcvd = 0; + } + + if (unlikely(mask)) { +#ifdef DEBUG_MODEM_IF + mif_info("%s: send RES_ACK 0x%04X\n", ld->name, mask); +#endif + send_int2cp(shmd, INT_NON_CMD(mask)); + } +} + +/** + * ipc_handler: processes a SHMEM command or receives IPC messages + * @shmd: pointer to an instance of shmem_link_device structure + * @mst: pointer to an instance of mem_status structure + * + * Invokes cmd_handler for a SHMEM command or msg_handler for IPC messages. + */ +static void ipc_handler(struct shmem_link_device *shmd, struct mem_status *mst) +{ +#ifdef DEBUG_MODEM_IF + struct link_device *ld = &shmd->ld; +#endif + u16 intr = mst->int2ap; + + if (unlikely(INT_CMD_VALID(intr))) { + cmd_handler(shmd, intr); + return; + } + + /* + ** Check REQ_ACK from CP -> REQ_ACK will be processed in the RX tasklet + */ + if (unlikely(intr & get_mask_req_ack(shmd, IPC_FMT))) + shmd->dev[IPC_FMT]->req_ack_rcvd = 1; + + if (unlikely(intr & get_mask_req_ack(shmd, IPC_RAW))) + shmd->dev[IPC_RAW]->req_ack_rcvd = 1; + + /* + ** Check and process RES_ACK from CP + */ + if (unlikely(intr & get_mask_res_ack(shmd, IPC_FMT))) { +#ifdef DEBUG_MODEM_IF + mif_info("%s: recv FMT RES_ACK\n", ld->name); +#endif + complete(&shmd->req_ack_cmpl[IPC_FMT]); + } + + if (unlikely(intr & get_mask_res_ack(shmd, IPC_RAW))) { +#ifdef DEBUG_MODEM_IF + mif_info("%s: recv RAW RES_ACK\n", ld->name); +#endif + complete(&shmd->req_ack_cmpl[IPC_RAW]); + } + + /* + ** Schedule RX tasklet + */ + tasklet_hi_schedule(&shmd->rx_tsk); +} + +/** + * rx_udl_frames + * @shmd: pointer to an instance of shmem_link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * @mst: pointer to an instance of mem_status structure + * + * Returns + * ret < 0 : error + * ret == 0 : ILLEGAL status + * ret > 0 : valid data + * + * Must be invoked only when there is data in the corresponding RXQ. + * + * Requires a recv_skb method in the io_device instance, so this function must + * be used for only SIPC5. + */ +static int rx_udl_frames(struct shmem_link_device *shmd, int dev, + struct circ_status *circ) +{ + struct link_device *ld = &shmd->ld; + struct io_device *iod; + struct sk_buff *skb; + int ret; + /** + * variables for the status of the circular queue + */ + u8 *src; + u8 hdr[SIPC5_MIN_HEADER_SIZE]; + /** + * variables for RX processing + */ + int qsize; /* size of the queue */ + int rcvd; /* size of data in the RXQ or error */ + int rest; /* size of the rest data */ + int out; /* index to the start of current frame */ + u8 *dst; /* pointer to the destination buffer */ + int tot; /* total length including padding data */ + + src = circ->buff; + qsize = circ->qsize; + out = circ->out; + rcvd = circ->size; + + rest = rcvd; + tot = 0; + while (rest > 0) { + /* Copy the header in the frame to the header buffer */ + circ_read(hdr, src, qsize, out, SIPC5_MIN_HEADER_SIZE); + + /* Check the config field in the header */ + if (unlikely(!sipc5_start_valid(hdr))) { + mif_err("%s: ERR! %s INVALID config 0x%02X " + "(rest %d, rcvd %d)\n", ld->name, + get_dev_name(dev), hdr[0], rest, rcvd); + pr_ipc(1, "UDL", (src + out), (rest > 20) ? 20 : rest); + ret = -EBADMSG; + goto exit; + } + + /* Verify the total length of the frame (data + padding) */ + tot = sipc5_get_total_len(hdr); + if (unlikely(tot > rest)) { + mif_err("%s: ERR! %s tot %d > rest %d (rcvd %d)\n", + ld->name, get_dev_name(dev), tot, rest, rcvd); + ret = -ENODATA; + goto exit; + } + + /* Allocate an skb */ + skb = alloc_skb(tot + NET_SKB_PAD, GFP_KERNEL); + if (!skb) { + mif_err("%s: ERR! %s alloc_skb fail\n", + ld->name, get_dev_name(dev)); + ret = -ENOMEM; + goto exit; + } + skb_reserve(skb, NET_SKB_PAD); + + /* Set the attribute of the skb as "single frame" */ + skbpriv(skb)->single_frame = true; + + /* Read the frame from the RXQ */ + dst = skb_put(skb, tot); + circ_read(dst, src, qsize, out, tot); + + /* Pass the skb to an iod */ + iod = link_get_iod_with_channel(ld, sipc5_get_ch_id(skb->data)); + if (!iod) { + mif_err("%s: ERR! no IPC_BOOT iod\n", ld->name); + break; + } + +#ifdef DEBUG_MODEM_IF + if (!std_udl_with_payload(std_udl_get_cmd(skb->data))) { + if (ld->mode == LINK_MODE_DLOAD) { + pr_ipc(0, "[CP->AP] DL CMD", skb->data, + (skb->len > 20 ? 20 : skb->len)); + } else { + pr_ipc(0, "[CP->AP] UL CMD", skb->data, + (skb->len > 20 ? 20 : skb->len)); + } + } +#endif + + iod->recv_skb(iod, ld, skb); + + /* Calculate new out value */ + rest -= tot; + out += tot; + if (unlikely(out >= qsize)) + out -= qsize; + } + + /* Update tail (out) pointer to empty out the RXQ */ + set_rxq_tail(shmd, dev, circ->in); + + return rcvd; + +exit: + return ret; +} + +/** + * udl_rx_work + * @ws: pointer to an instance of the work_struct structure + * + * Invokes the recv method in the io_device instance to perform receiving IPC + * messages from each skb. + */ +static void udl_rx_work(struct work_struct *ws) +{ + struct shmem_link_device *shmd; + struct link_device *ld; + struct sk_buff_head *rxq; + struct mem_status mst; + struct circ_status circ; + int dev = IPC_RAW; + + shmd = container_of(ws, struct shmem_link_device, udl_rx_dwork.work); + ld = &shmd->ld; + rxq = ld->skb_rxq[dev]; + + while (1) { + get_shmem_status(shmd, RX, &mst); + if (mst.head[dev][RX] == mst.tail[dev][RX]) + break; + + /* Invoke an RX function only when there is data in the RXQ */ + if (get_rxq_rcvd(shmd, dev, &mst, &circ) < 0) { + mif_err("%s: ERR! get_rxq_rcvd fail\n", ld->name); +#ifdef DEBUG_MODEM_IF + trigger_forced_cp_crash(shmd); +#endif + break; + } + + if (rx_udl_frames(shmd, dev, &circ) < 0) { + skb_queue_purge(rxq); + break; + } + } +} + +/** + * udl_handler: receives BOOT/DUMP IPC messages from every RXQ + * @shmd: pointer to an instance of shmem_link_device structure + * @mst: pointer to an instance of mem_status structure + * + * 1) Receives all IPC message frames currently in every IPC RXQ. + * 2) Sends RES_ACK responses if there are REQ_ACK requests from a CP. + * 3) Completes all threads waiting for the corresponding RES_ACK from a CP if + * there is any RES_ACK response. + */ +static void udl_handler(struct shmem_link_device *shmd, struct mem_status *mst) +{ + u16 intr = mst->int2ap; + + /* Schedule soft IRQ for RX */ + queue_delayed_work(system_nrt_wq, &shmd->udl_rx_dwork, 0); + + /* Check and process RES_ACK */ + if (intr & INT_MASK_RES_ACK_SET) { + if (intr & get_mask_res_ack(shmd, IPC_RAW)) { +#ifdef DEBUG_MODEM_IF + struct link_device *ld = &shmd->ld; + mif_info("%s: recv RAW RES_ACK\n", ld->name); + print_circ_status(ld, IPC_RAW, mst); +#endif + complete(&shmd->req_ack_cmpl[IPC_RAW]); + } + } +} + +/** + * c2c_irq_handler: interrupt handler for a C2C interrupt + * @data: pointer to a data + * + * 1) Reads the interrupt value + * 2) Performs interrupt handling + * + * Flow for normal interrupt handling: + * c2c_irq_handler -> udl_handler + * c2c_irq_handler -> ipc_handler -> cmd_handler -> cmd_xxx_handler + * c2c_irq_handler -> ipc_handler -> msg_handler -> rx_ipc_frames -> ... + */ +static void c2c_irq_handler(void *data, u32 intr) +{ + struct shmem_link_device *shmd = (struct shmem_link_device *)data; + struct link_device *ld = (struct link_device *)&shmd->ld; + struct mem_status *mst = msq_get_free_slot(&shmd->stat_list); + + get_shmem_status(shmd, RX, mst); + + if (unlikely(ld->mode == LINK_MODE_OFFLINE)) { + mif_info("%s: ld->mode == LINK_MODE_OFFLINE\n", ld->name); + return; + } + + if (unlikely(!INT_VALID(intr))) { + mif_info("%s: ERR! invalid intr 0x%X\n", ld->name, intr); + return; + } + + mst->int2ap = intr; + + if (ld->mode == LINK_MODE_DLOAD || ld->mode == LINK_MODE_ULOAD) + udl_handler(shmd, mst); + else + ipc_handler(shmd, mst); +} + +#if 1 +/* Functions for IPC/BOOT/DUMP TX */ +#endif + +/** + * write_ipc_to_txq + * @shmd: pointer to an instance of shmem_link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * @circ: pointer to an instance of circ_status structure + * @skb: pointer to an instance of sk_buff structure + * + * Must be invoked only when there is enough space in the TXQ. + */ +static void write_ipc_to_txq(struct shmem_link_device *shmd, int dev, + struct circ_status *circ, struct sk_buff *skb) +{ + u32 qsize = circ->qsize; + u32 in = circ->in; + u8 *buff = circ->buff; + u8 *src = skb->data; + u32 len = skb->len; +#ifdef DEBUG_MODEM_IF + struct io_device *iod = skbpriv(skb)->iod; + struct modem_ctl *mc = shmd->ld.mc; + char tag[MIF_MAX_STR_LEN]; + + snprintf(tag, MIF_MAX_STR_LEN, "LNK: %s->%s", iod->name, mc->name); + + if (dev == IPC_FMT) + pr_ipc(1, tag, src, (len > 20 ? 20 : len)); +#if 0 + if (dev == IPC_RAW) + pr_ipc(0, tag, src, (len > 20 ? 20 : len)); +#endif +#endif + + /* Write data to the TXQ */ + circ_write(buff, src, qsize, in, len); + + /* Update new head (in) pointer */ + set_txq_head(shmd, dev, circ_new_pointer(qsize, in, len)); +} + +/** + * xmit_ipc_msg + * @shmd: pointer to an instance of shmem_link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Tries to transmit IPC messages in the skb_txq of @dev as many as possible. + * + * Returns total length of IPC messages transmitted or an error code. + */ +static int xmit_ipc_msg(struct shmem_link_device *shmd, int dev) +{ + struct link_device *ld = &shmd->ld; + struct sk_buff_head *txq = ld->skb_txq[dev]; + struct sk_buff *skb; + unsigned long flags; + struct circ_status circ; + int space; + int copied = 0; + + /* Acquire the spin lock for a TXQ */ + spin_lock_irqsave(&shmd->tx_lock[dev], flags); + + while (1) { + /* Get the size of free space in the TXQ */ + space = get_txq_space(shmd, dev, &circ); + if (unlikely(space < 0)) { +#ifdef DEBUG_MODEM_IF + /* Trigger a enforced CP crash */ + trigger_forced_cp_crash(shmd); +#endif + /* Empty out the TXQ */ + reset_txq_circ(shmd, dev); + copied = -EIO; + break; + } + + skb = skb_dequeue(txq); + if (unlikely(!skb)) + break; + + /* Check the free space size comparing with skb->len */ + if (unlikely(space < skb->len)) { +#ifdef DEBUG_MODEM_IF + struct mem_status mst; +#endif + /* Set res_required flag for the "dev" */ + atomic_set(&shmd->res_required[dev], 1); + + /* Take the skb back to the skb_txq */ + skb_queue_head(txq, skb); + + mif_err("%s: <by %pf> NOSPC in %s_TXQ" + "{qsize:%u in:%u out:%u} free:%u < len:%u\n", + ld->name, CALLER, get_dev_name(dev), + circ.qsize, circ.in, circ.out, space, skb->len); +#ifdef DEBUG_MODEM_IF + get_shmem_status(shmd, TX, &mst); + print_circ_status(ld, dev, &mst); +#endif + copied = -ENOSPC; + break; + } + +#ifdef DEBUG_MODEM_IF + if (!ipc_active(shmd)) { + if (get_magic(shmd) == SHM_PM_MAGIC) { + mif_err("%s: Going to SLEEP\n", ld->name); + copied = -EBUSY; + } else { + mif_err("%s: IPC is NOT active\n", ld->name); + copied = -EIO; + } + break; + } +#endif + + /* TX only when there is enough space in the TXQ */ + write_ipc_to_txq(shmd, dev, &circ, skb); + copied += skb->len; + dev_kfree_skb_any(skb); + } + + /* Release the spin lock */ + spin_unlock_irqrestore(&shmd->tx_lock[dev], flags); + + return copied; +} + +/** + * wait_for_res_ack + * @shmd: pointer to an instance of shmem_link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * 1) Sends an REQ_ACK interrupt for @dev to CP. + * 2) Waits for the corresponding RES_ACK for @dev from CP. + * + * Returns the return value from wait_for_completion_interruptible_timeout(). + */ +static int wait_for_res_ack(struct shmem_link_device *shmd, int dev) +{ + struct link_device *ld = &shmd->ld; + struct completion *cmpl = &shmd->req_ack_cmpl[dev]; + unsigned long timeout = msecs_to_jiffies(RES_ACK_WAIT_TIMEOUT); + int ret; + u16 mask; + +#ifdef DEBUG_MODEM_IF + mif_info("%s: send %s REQ_ACK\n", ld->name, get_dev_name(dev)); +#endif + + mask = get_mask_req_ack(shmd, dev); + send_int2cp(shmd, INT_NON_CMD(mask)); + + /* ret < 0 if interrupted, ret == 0 on timeout */ + ret = wait_for_completion_interruptible_timeout(cmpl, timeout); + if (ret < 0) { + mif_err("%s: %s: wait_for_completion interrupted! (ret %d)\n", + ld->name, get_dev_name(dev), ret); + goto exit; + } + + if (ret == 0) { + struct mem_status mst; + get_shmem_status(shmd, TX, &mst); + + mif_err("%s: wait_for_completion TIMEOUT! (no %s_RES_ACK)\n", + ld->name, get_dev_name(dev)); + + /* + ** The TXQ must be checked whether or not it is empty, because + ** an interrupt mask can be overwritten by the next interrupt. + */ + if (mst.head[dev][TX] == mst.tail[dev][TX]) { + ret = get_txq_buff_size(shmd, dev); +#ifdef DEBUG_MODEM_IF + mif_err("%s: %s_TXQ has been emptied\n", + ld->name, get_dev_name(dev)); + print_circ_status(ld, dev, &mst); +#endif + } + } + +exit: + return ret; +} + +/** + * process_res_ack + * @shmd: pointer to an instance of shmem_link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * 1) Tries to transmit IPC messages in the skb_txq with xmit_ipc_msg(). + * 2) Sends an interrupt to CP if there is no error from xmit_ipc_msg(). + * 3) Restarts SHMEM flow control if xmit_ipc_msg() returns -ENOSPC. + * + * Returns the return value from xmit_ipc_msg(). + */ +static int process_res_ack(struct shmem_link_device *shmd, int dev) +{ + int ret; + u16 mask; + + ret = xmit_ipc_msg(shmd, dev); + if (ret > 0) { + mask = get_mask_send(shmd, dev); + send_int2cp(shmd, INT_NON_CMD(mask)); + get_shmem_status(shmd, TX, msq_get_free_slot(&shmd->stat_list)); + } + + if (ret >= 0) + atomic_set(&shmd->res_required[dev], 0); + + return ret; +} + +/** + * fmt_tx_work: performs TX for FMT IPC device under SHMEM flow control + * @ws: pointer to an instance of the work_struct structure + * + * 1) Starts waiting for RES_ACK of FMT IPC device. + * 2) Returns immediately if the wait is interrupted. + * 3) Restarts SHMEM flow control if there is a timeout from the wait. + * 4) Otherwise, it performs processing RES_ACK for FMT IPC device. + */ +static void fmt_tx_work(struct work_struct *ws) +{ + struct link_device *ld; + struct shmem_link_device *shmd; + int ret; + + ld = container_of(ws, struct link_device, fmt_tx_dwork.work); + shmd = to_shmem_link_device(ld); + + ret = wait_for_res_ack(shmd, IPC_FMT); + /* ret < 0 if interrupted */ + if (ret < 0) + return; + + /* ret == 0 on timeout */ + if (ret == 0) { + queue_delayed_work(ld->tx_wq, ld->tx_dwork[IPC_FMT], 0); + return; + } + + ret = process_res_ack(shmd, IPC_FMT); + if (ret >= 0) { + permit_cp_sleep(shmd); + return; + } + + /* At this point, ret < 0 */ + if (ret == -ENOSPC || ret == -EBUSY) { + queue_delayed_work(ld->tx_wq, ld->tx_dwork[IPC_FMT], + msecs_to_jiffies(1)); + } +} + +/** + * raw_tx_work: performs TX for RAW IPC device under SHMEM flow control. + * @ws: pointer to an instance of the work_struct structure + * + * 1) Starts waiting for RES_ACK of RAW IPC device. + * 2) Returns immediately if the wait is interrupted. + * 3) Restarts SHMEM flow control if there is a timeout from the wait. + * 4) Otherwise, it performs processing RES_ACK for RAW IPC device. + */ +static void raw_tx_work(struct work_struct *ws) +{ + struct link_device *ld; + struct shmem_link_device *shmd; + int ret; + + ld = container_of(ws, struct link_device, raw_tx_dwork.work); + shmd = to_shmem_link_device(ld); + + ret = wait_for_res_ack(shmd, IPC_RAW); + /* ret < 0 if interrupted */ + if (ret < 0) + return; + + /* ret == 0 on timeout */ + if (ret == 0) { + queue_delayed_work(ld->tx_wq, ld->tx_dwork[IPC_RAW], 0); + return; + } + + ret = process_res_ack(shmd, IPC_RAW); + if (ret >= 0) { + permit_cp_sleep(shmd); + mif_netif_wake(ld); + return; + } + + /* At this point, ret < 0 */ + if (ret == -ENOSPC || ret == -EBUSY) { + queue_delayed_work(ld->tx_wq, ld->tx_dwork[IPC_RAW], + msecs_to_jiffies(1)); + } +} + +/** + * c2c_send_ipc + * @shmd: pointer to an instance of shmem_link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * @skb: pointer to an skb that will be transmitted + * + * 1) Tries to transmit IPC messages in the skb_txq with xmit_ipc_msg(). + * 2) Sends an interrupt to CP if there is no error from xmit_ipc_msg(). + * 3) Starts SHMEM flow control if xmit_ipc_msg() returns -ENOSPC. + */ +static int c2c_send_ipc(struct shmem_link_device *shmd, int dev) +{ + struct link_device *ld = &shmd->ld; + int ret; + u16 mask; + + if (atomic_read(&shmd->res_required[dev]) > 0) { + mif_info("%s: %s_TXQ is full\n", ld->name, get_dev_name(dev)); + return 0; + } + + ret = xmit_ipc_msg(shmd, dev); + if (likely(ret > 0)) { + mask = get_mask_send(shmd, dev); + send_int2cp(shmd, INT_NON_CMD(mask)); + get_shmem_status(shmd, TX, msq_get_free_slot(&shmd->stat_list)); + goto exit; + } + + /* If there was no TX, just exit */ + if (ret == 0) + goto exit; + + /* At this point, ret < 0 */ + if (ret == -ENOSPC || ret == -EBUSY) { + /* Prohibit CP from sleeping until the TXQ buffer is empty */ + if (forbid_cp_sleep(shmd) < 0) { + trigger_forced_cp_crash(shmd); + goto exit; + } + + /*----------------------------------------------------*/ + /* shmd->res_required[dev] was set in xmit_ipc_msg(). */ + /*----------------------------------------------------*/ + + if (dev == IPC_RAW) + mif_netif_stop(ld); + + queue_delayed_work(ld->tx_wq, ld->tx_dwork[dev], + msecs_to_jiffies(1)); + } + +exit: + return ret; +} + +/** + * c2c_try_send_ipc + * @shmd: pointer to an instance of shmem_link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * @iod: pointer to an instance of the io_device structure + * @skb: pointer to an skb that will be transmitted + * + * 1) Enqueues an skb to the skb_txq for @dev in the link device instance. + * 2) Tries to transmit IPC messages with c2c_send_ipc(). + */ +static void c2c_try_send_ipc(struct shmem_link_device *shmd, int dev, + struct io_device *iod, struct sk_buff *skb) +{ + struct link_device *ld = &shmd->ld; + struct sk_buff_head *txq = ld->skb_txq[dev]; + int ret; + + if (forbid_cp_sleep(shmd) < 0) { + trigger_forced_cp_crash(shmd); + goto exit; + } + + if (unlikely(txq->qlen >= MAX_SKB_TXQ_DEPTH)) { + mif_err("%s: %s txq->qlen %d >= %d\n", ld->name, + get_dev_name(dev), txq->qlen, MAX_SKB_TXQ_DEPTH); + dev_kfree_skb_any(skb); + goto exit; + } + + skb_queue_tail(txq, skb); + + ret = c2c_send_ipc(shmd, dev); + if (ret < 0) { + mif_err("%s->%s: ERR! c2c_send_ipc fail (err %d)\n", + iod->name, ld->name, ret); + } + +exit: + permit_cp_sleep(shmd); +} + +static int c2c_send_udl_cmd(struct shmem_link_device *shmd, int dev, + struct io_device *iod, struct sk_buff *skb) +{ + struct link_device *ld = &shmd->ld; + u8 *buff; + u8 *src; + u32 qsize; + u32 in; + int space; + int tx_bytes; + struct circ_status circ; + + if (iod->format == IPC_BOOT) { + pr_ipc(0, "[AP->CP] DL CMD", skb->data, + (skb->len > 20 ? 20 : skb->len)); + } else { + pr_ipc(0, "[AP->CP] UL CMD", skb->data, + (skb->len > 20 ? 20 : skb->len)); + } + + /* Get the size of free space in the TXQ */ + space = get_txq_space(shmd, dev, &circ); + if (space < 0) { + reset_txq_circ(shmd, dev); + tx_bytes = -EIO; + goto exit; + } + + /* Get the size of data to be sent */ + tx_bytes = skb->len; + + /* Check the size of free space */ + if (space < tx_bytes) { + mif_err("%s: NOSPC in %s_TXQ {qsize:%u in:%u out:%u}, " + "free:%u < tx_bytes:%u\n", ld->name, get_dev_name(dev), + circ.qsize, circ.in, circ.out, space, tx_bytes); + tx_bytes = -ENOSPC; + goto exit; + } + + /* Write data to the TXQ */ + buff = circ.buff; + src = skb->data; + qsize = circ.qsize; + in = circ.in; + circ_write(buff, src, qsize, in, tx_bytes); + + /* Update new head (in) pointer */ + set_txq_head(shmd, dev, circ_new_pointer(qsize, circ.in, tx_bytes)); + +exit: + dev_kfree_skb_any(skb); + return tx_bytes; +} + +static int c2c_send_udl_data(struct shmem_link_device *shmd, int dev) +{ + struct link_device *ld = &shmd->ld; + struct sk_buff_head *txq = ld->skb_txq[dev]; + struct sk_buff *skb; + u8 *src; + int tx_bytes; + int copied; + u8 *buff; + u32 qsize; + u32 in; + u32 out; + int space; + struct circ_status circ; + + /* Get the size of free space in the TXQ */ + space = get_txq_space(shmd, dev, &circ); + if (space < 0) { +#ifdef DEBUG_MODEM_IF + /* Trigger a enforced CP crash */ + trigger_forced_cp_crash(shmd); +#endif + /* Empty out the TXQ */ + reset_txq_circ(shmd, dev); + return -EFAULT; + } + + buff = circ.buff; + qsize = circ.qsize; + in = circ.in; + out = circ.out; + space = circ.size; + + copied = 0; + while (1) { + skb = skb_dequeue(txq); + if (!skb) + break; + + /* Get the size of data to be sent */ + src = skb->data; + tx_bytes = skb->len; + + /* Check the free space size comparing with skb->len */ + if (space < tx_bytes) { + /* Set res_required flag for the "dev" */ + atomic_set(&shmd->res_required[dev], 1); + + /* Take the skb back to the skb_txq */ + skb_queue_head(txq, skb); + + mif_info("NOSPC in RAW_TXQ {qsize:%u in:%u out:%u}, " + "space:%u < tx_bytes:%u\n", + qsize, in, out, space, tx_bytes); + break; + } + + /* + ** TX only when there is enough space in the TXQ + */ + circ_write(buff, src, qsize, in, tx_bytes); + + copied += tx_bytes; + in = circ_new_pointer(qsize, in, tx_bytes); + space -= tx_bytes; + + dev_kfree_skb_any(skb); + } + + /* Update new head (in) pointer */ + if (copied > 0) { + in = circ_new_pointer(qsize, circ.in, copied); + set_txq_head(shmd, dev, in); + } + + return copied; +} + +/** + * c2c_send_udl + * @shmd: pointer to an instance of shmem_link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * @iod: pointer to an instance of the io_device structure + * @skb: pointer to an skb that will be transmitted + * + * 1) Enqueues an skb to the skb_txq for @dev in the link device instance. + * 2) Tries to transmit IPC messages in the skb_txq by invoking xmit_ipc_msg() + * function. + * 3) Sends an interrupt to CP if there is no error from xmit_ipc_msg(). + * 4) Starts SHMEM flow control if xmit_ipc_msg() returns -ENOSPC. + */ +static void c2c_send_udl(struct shmem_link_device *shmd, int dev, + struct io_device *iod, struct sk_buff *skb) +{ + struct link_device *ld = &shmd->ld; + struct sk_buff_head *txq = ld->skb_txq[dev]; + struct completion *cmpl = &shmd->req_ack_cmpl[dev]; + struct std_dload_info *dl_info = &shmd->dl_info; + struct mem_status mst; + u32 timeout = msecs_to_jiffies(RES_ACK_WAIT_TIMEOUT); + u32 udl_cmd; + int ret; + u16 mask = get_mask_send(shmd, dev); + + udl_cmd = std_udl_get_cmd(skb->data); + if (iod->format == IPC_RAMDUMP || !std_udl_with_payload(udl_cmd)) { + ret = c2c_send_udl_cmd(shmd, dev, iod, skb); + if (ret > 0) + send_int2cp(shmd, INT_NON_CMD(mask)); + else + mif_err("ERR! c2c_send_udl_cmd fail (err %d)\n", ret); + goto exit; + } + + skb_queue_tail(txq, skb); + if (txq->qlen < dl_info->num_frames) + goto exit; + + mask |= get_mask_req_ack(shmd, dev); + while (1) { + ret = c2c_send_udl_data(shmd, dev); + if (ret < 0) { + mif_err("ERR! c2c_send_udl_data fail (err %d)\n", ret); + skb_queue_purge(txq); + break; + } + + if (skb_queue_empty(txq)) { + send_int2cp(shmd, INT_NON_CMD(mask)); + break; + } + + send_int2cp(shmd, INT_NON_CMD(mask)); + + do { + ret = wait_for_completion_timeout(cmpl, timeout); + get_shmem_status(shmd, TX, &mst); + } while (mst.head[dev][TX] != mst.tail[dev][TX]); + } + +exit: + return; +} + +/** + * c2c_send + * @ld: pointer to an instance of the link_device structure + * @iod: pointer to an instance of the io_device structure + * @skb: pointer to an skb that will be transmitted + * + * Returns the length of data transmitted or an error code. + * + * Normal call flow for an IPC message: + * c2c_try_send_ipc -> c2c_send_ipc -> xmit_ipc_msg -> write_ipc_to_txq + * + * Call flow on congestion in a IPC TXQ: + * c2c_try_send_ipc -> c2c_send_ipc -> xmit_ipc_msg ,,, queue_delayed_work + * => xxx_tx_work -> wait_for_res_ack + * => msg_handler + * => process_res_ack -> xmit_ipc_msg (,,, queue_delayed_work ...) + */ +static int c2c_send(struct link_device *ld, struct io_device *iod, + struct sk_buff *skb) +{ + struct shmem_link_device *shmd = to_shmem_link_device(ld); + int dev = iod->format; + int len = skb->len; + + switch (dev) { + case IPC_FMT: + case IPC_RAW: + if (likely(ld->mode == LINK_MODE_IPC)) { + c2c_try_send_ipc(shmd, dev, iod, skb); + } else { + mif_err("%s->%s: ERR! ld->mode != LINK_MODE_IPC\n", + iod->name, ld->name); + dev_kfree_skb_any(skb); + } + return len; + + case IPC_BOOT: + case IPC_RAMDUMP: + c2c_send_udl(shmd, IPC_RAW, iod, skb); + return len; + + default: + mif_err("%s: ERR! no TXQ for %s\n", ld->name, iod->name); + dev_kfree_skb_any(skb); + return -ENODEV; + } +} + +#if 1 +/* Functions for BOOT/DUMP and INIT */ +#endif + +static int c2c_dload_start(struct link_device *ld, struct io_device *iod) +{ + struct shmem_link_device *shmd = to_shmem_link_device(ld); + u32 magic; + + ld->mode = LINK_MODE_DLOAD; + + clear_shmem_map(shmd); + + set_magic(shmd, SHM_BOOT_MAGIC); + magic = get_magic(shmd); + if (magic != SHM_BOOT_MAGIC) { + mif_err("%s: ERR! magic 0x%08X != SHM_BOOT_MAGIC 0x%08X\n", + ld->name, magic, SHM_BOOT_MAGIC); + return -EFAULT; + } + + return 0; +} + +/** + * c2c_set_dload_info + * @ld: pointer to an instance of link_device structure + * @iod: pointer to an instance of io_device structure + * @arg: pointer to an instance of std_dload_info structure in "user" memory + * + */ +static int c2c_set_dload_info(struct link_device *ld, struct io_device *iod, + unsigned long arg) +{ + struct shmem_link_device *shmd = to_shmem_link_device(ld); + struct std_dload_info *dl_info = &shmd->dl_info; + int ret; + + ret = copy_from_user(dl_info, (void __user *)arg, + sizeof(struct std_dload_info)); + if (ret) { + mif_err("ERR! copy_from_user fail!\n"); + return -EFAULT; + } + + return 0; +} + +static int c2c_force_dump(struct link_device *ld, struct io_device *iod) +{ + struct shmem_link_device *shmd = to_shmem_link_device(ld); + mif_err("+++\n"); + trigger_forced_cp_crash(shmd); + mif_err("---\n"); + return 0; +} + +static int c2c_dump_start(struct link_device *ld, struct io_device *iod) +{ + struct shmem_link_device *shmd = to_shmem_link_device(ld); + + ld->mode = LINK_MODE_ULOAD; + + clear_shmem_map(shmd); + + mif_err("%s: magic = 0x%08X\n", ld->name, SHM_DUMP_MAGIC); + set_magic(shmd, SHM_DUMP_MAGIC); + + return 0; +} + +static void c2c_remap_4mb_ipc_region(struct shmem_link_device *shmd) +{ + struct shmem_4mb_phys_map *map; + struct shmem_ipc_device *dev; + + map = (struct shmem_4mb_phys_map *)shmd->base; + + /* Magic code and access enable fields */ + shmd->ipc_map.magic = (u32 __iomem *)&map->magic; + shmd->ipc_map.access = (u32 __iomem *)&map->access; + + /* FMT */ + dev = &shmd->ipc_map.dev[IPC_FMT]; + + strcpy(dev->name, "FMT"); + dev->id = IPC_FMT; + + dev->txq.head = (u32 __iomem *)&map->fmt_tx_head; + dev->txq.tail = (u32 __iomem *)&map->fmt_tx_tail; + dev->txq.buff = (u8 __iomem *)&map->fmt_tx_buff[0]; + dev->txq.size = SHM_4M_FMT_TX_BUFF_SZ; + + dev->rxq.head = (u32 __iomem *)&map->fmt_rx_head; + dev->rxq.tail = (u32 __iomem *)&map->fmt_rx_tail; + dev->rxq.buff = (u8 __iomem *)&map->fmt_rx_buff[0]; + dev->rxq.size = SHM_4M_FMT_RX_BUFF_SZ; + + 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 = &shmd->ipc_map.dev[IPC_RAW]; + + strcpy(dev->name, "RAW"); + dev->id = IPC_RAW; + + dev->txq.head = (u32 __iomem *)&map->raw_tx_head; + dev->txq.tail = (u32 __iomem *)&map->raw_tx_tail; + dev->txq.buff = (u8 __iomem *)&map->raw_tx_buff[0]; + dev->txq.size = SHM_4M_RAW_TX_BUFF_SZ; + + dev->rxq.head = (u32 __iomem *)&map->raw_rx_head; + dev->rxq.tail = (u32 __iomem *)&map->raw_rx_tail; + dev->rxq.buff = (u8 __iomem *)&map->raw_rx_buff[0]; + dev->rxq.size = SHM_4M_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 */ + shmd->ipc_map.mbx2ap = NULL; + shmd->ipc_map.mbx2cp = NULL; +} + +static int c2c_init_ipc_map(struct shmem_link_device *shmd) +{ + if (shmd->size >= SHMEM_SIZE_4MB) + c2c_remap_4mb_ipc_region(shmd); + else + return -EINVAL; + + memset(shmd->base, 0, shmd->size); + + shmd->magic = shmd->ipc_map.magic; + shmd->access = shmd->ipc_map.access; + + shmd->dev[IPC_FMT] = &shmd->ipc_map.dev[IPC_FMT]; + shmd->dev[IPC_RAW] = &shmd->ipc_map.dev[IPC_RAW]; + + shmd->mbx2ap = shmd->ipc_map.mbx2ap; + shmd->mbx2cp = shmd->ipc_map.mbx2cp; + + return 0; +} +static int c2c_init_communication(struct link_device *ld, struct io_device *iod) +{ + struct shmem_link_device *shmd = to_shmem_link_device(ld); + struct io_device *check_iod; + + if (iod->format == IPC_BOOT) + return 0; + + /* send 0xC2 */ + switch(iod->format) { + case IPC_FMT: + check_iod = link_get_iod_with_format(ld, IPC_RFS); + if (check_iod ? atomic_read(&check_iod->opened) : true) { + mif_err("%s: Send 0xC2 (INIT_END)\n", ld->name); + send_int2cp(shmd, INT_CMD(INT_CMD_INIT_END)); + } else + mif_err("%s defined but not opened\n", check_iod->name); + break; + case IPC_RFS: + check_iod = link_get_iod_with_format(ld, IPC_FMT); + if (check_iod && atomic_read(&check_iod->opened)) { + mif_err("%s: Send 0xC2 (INIT_END)\n", ld->name); + send_int2cp(shmd, INT_CMD(INT_CMD_INIT_END)); + } else + mif_err("not opened\n"); + break; + default: + break; + } + return 0; +} + +#if 0 +static void c2c_link_terminate(struct link_device *ld, struct io_device *iod) +{ + if (iod->format == IPC_FMT && ld->mode == LINK_MODE_IPC) { + if (!atomic_read(&iod->opened)) { + ld->mode = LINK_MODE_OFFLINE; + mif_err("%s: %s: link mode changed: IPC -> OFFLINE\n", + iod->name, ld->name); + } + } + + return; +} +#endif + struct link_device *c2c_create_link_device(struct platform_device *pdev) { - struct c2c_link_device *dpld; + struct modem_data *modem; + struct shmem_link_device *shmd; struct link_device *ld; - struct modem_data *pdata; + int err; + int i; + u32 phys_base; + u32 offset; + u32 size; + int irq; + unsigned long irq_flags; + char name[MIF_MAX_NAME_LEN]; - pdata = pdev->dev.platform_data; + /* + ** Get the modem (platform) data + */ + modem = (struct modem_data *)pdev->dev.platform_data; + if (!modem) { + mif_err("ERR! modem == NULL\n"); + return NULL; + } + mif_err("%s: %s\n", modem->link_name, modem->name); - dpld = kzalloc(sizeof(struct c2c_link_device), GFP_KERNEL); - if (!dpld) { - mif_err("dpld == NULL\n"); + if (modem->ipc_version < SIPC_VER_50) { + mif_err("%s: %s: ERR! IPC version %d < SIPC_VER_50\n", + modem->link_name, modem->name, modem->ipc_version); return NULL; } - wake_lock_init(&dpld->c2c_wake_lock, WAKE_LOCK_SUSPEND, "c2c_wakelock"); - wake_lock(&dpld->c2c_wake_lock); + /* + ** Alloc an instance of shmem_link_device structure + */ + shmd = kzalloc(sizeof(struct shmem_link_device), GFP_KERNEL); + if (!shmd) { + mif_err("%s: ERR! shmd kzalloc fail\n", modem->link_name); + goto error; + } + ld = &shmd->ld; + + /* + ** Retrieve modem data and SHMEM control data from the modem data + */ + ld->mdm_data = modem; + ld->name = modem->link_name; + ld->aligned = 1; + ld->ipc_version = modem->ipc_version; + ld->max_ipc_dev = MAX_SIPC5_DEV; + + /* + ** Set attributes as a link device + */ + ld->init_comm = c2c_init_communication; +#if 0 + ld->terminate_comm = c2c_link_terminate; +#endif + ld->send = c2c_send; + ld->dload_start = c2c_dload_start; + ld->firm_update = c2c_set_dload_info; + ld->force_dump = c2c_force_dump; + ld->dump_start = c2c_dump_start; + + INIT_LIST_HEAD(&ld->list); - ld = &dpld->ld; - dpld->pdata = pdata; + skb_queue_head_init(&ld->sk_fmt_tx_q); + skb_queue_head_init(&ld->sk_raw_tx_q); + ld->skb_txq[IPC_FMT] = &ld->sk_fmt_tx_q; + ld->skb_txq[IPC_RAW] = &ld->sk_raw_tx_q; - ld->name = "c2c"; + skb_queue_head_init(&ld->sk_fmt_rx_q); + skb_queue_head_init(&ld->sk_raw_rx_q); + ld->skb_rxq[IPC_FMT] = &ld->sk_fmt_rx_q; + ld->skb_rxq[IPC_RAW] = &ld->sk_raw_rx_q; - mif_info("%s is created!!!\n", dpld->ld.name); + init_completion(&ld->init_cmpl); + + /* + ** Retrieve SHMEM resource + */ + if (modem->link_types & LINKTYPE(LINKDEV_C2C)) { + shmd->type = C2C_SHMEM; + mif_debug("%s: shmd->type = C2C_SHMEM\n", ld->name); + } else if (modem->link_types & LINKTYPE(LINKDEV_SHMEM)) { + shmd->type = REAL_SHMEM; + mif_debug("%s: shmd->type = REAL_SHMEM\n", ld->name); + } else { + mif_err("%s: ERR! invalid type\n", ld->name); + goto error; + } + + phys_base = c2c_get_phys_base(); + offset = c2c_get_sh_rgn_offset(); + size = c2c_get_sh_rgn_size(); + mif_debug("%s: phys_base:0x%08X offset:0x%08X size:%d\n", + ld->name, phys_base, offset, size); + + shmd->start = phys_base + offset; + shmd->size = size; + shmd->base = c2c_request_sh_region(shmd->start, shmd->size); + if (!shmd->base) { + mif_err("%s: ERR! c2c_request_sh_region fail\n", ld->name); + goto error; + } + + mif_debug("%s: phys_addr:0x%08X virt_addr:0x%08X size:%d\n", + ld->name, shmd->start, (int)shmd->base, shmd->size); + + /* + ** Initialize SHMEM maps (physical map -> logical map) + */ + err = c2c_init_ipc_map(shmd); + if (err < 0) { + mif_err("%s: ERR! c2c_init_ipc_map fail (err %d)\n", + ld->name, err); + goto error; + } + + /* + ** Initialize locks, completions, and bottom halves + */ + sprintf(shmd->wlock_name, "%s_wlock", ld->name); + wake_lock_init(&shmd->wlock, WAKE_LOCK_SUSPEND, shmd->wlock_name); + + sprintf(shmd->ap_wlock_name, "%s_ap_wlock", ld->name); + wake_lock_init(&shmd->ap_wlock, WAKE_LOCK_SUSPEND, shmd->ap_wlock_name); + + sprintf(shmd->cp_wlock_name, "%s_cp_wlock", ld->name); + wake_lock_init(&shmd->cp_wlock, WAKE_LOCK_SUSPEND, shmd->cp_wlock_name); + + init_completion(&shmd->udl_cmpl); + for (i = 0; i < MAX_SIPC5_DEV; i++) + init_completion(&shmd->req_ack_cmpl[i]); + + tasklet_init(&shmd->rx_tsk, msg_rx_task, (unsigned long)shmd); + INIT_DELAYED_WORK(&shmd->ipc_rx_dwork, ipc_rx_work); + INIT_DELAYED_WORK(&shmd->udl_rx_dwork, udl_rx_work); + + for (i = 0; i < MAX_SIPC5_DEV; i++) { + spin_lock_init(&shmd->tx_lock[i]); + atomic_set(&shmd->res_required[i], 0); + } + + ld->tx_wq = create_singlethread_workqueue("shmem_tx_wq"); + if (!ld->tx_wq) { + mif_err("%s: ERR! fail to create tx_wq\n", ld->name); + goto error; + } + INIT_DELAYED_WORK(&ld->fmt_tx_dwork, fmt_tx_work); + INIT_DELAYED_WORK(&ld->raw_tx_dwork, raw_tx_work); + ld->tx_dwork[IPC_FMT] = &ld->fmt_tx_dwork; + ld->tx_dwork[IPC_RAW] = &ld->raw_tx_dwork; + + spin_lock_init(&shmd->stat_list.lock); + spin_lock_init(&shmd->trace_list.lock); +#ifdef DEBUG_MODEM_IF + INIT_DELAYED_WORK(&shmd->dump_dwork, mem_dump_work); +#endif + + INIT_DELAYED_WORK(&shmd->cp_sleep_dwork, release_cp_wakeup); + INIT_DELAYED_WORK(&shmd->link_off_dwork, release_ap_status); + spin_lock_init(&shmd->pm_lock); + + /* + ** Retrieve SHMEM IRQ GPIO#, IRQ#, and IRQ flags + */ + shmd->gpio_pda_active = modem->gpio_pda_active; + mif_err("PDA_ACTIVE gpio# = %d (value %d)\n", + shmd->gpio_pda_active, gpio_get_value(shmd->gpio_pda_active)); + + shmd->gpio_ap_status = modem->gpio_ap_status; + shmd->gpio_ap_wakeup = modem->gpio_ap_wakeup; + shmd->irq_ap_wakeup = modem->irq_ap_wakeup; + if (!shmd->irq_ap_wakeup) { + mif_err("ERR! no irq_ap_wakeup\n"); + goto error; + } + mif_debug("CP2AP_WAKEUP IRQ# = %d\n", shmd->irq_ap_wakeup); + + shmd->gpio_cp_wakeup = modem->gpio_cp_wakeup; + shmd->gpio_cp_status = modem->gpio_cp_status; + shmd->irq_cp_status = modem->irq_cp_status; + if (!shmd->irq_cp_status) { + mif_err("ERR! no irq_cp_status\n"); + goto error; + } + mif_debug("CP2AP_STATUS IRQ# = %d\n", shmd->irq_cp_status); + + c2c_assign_gpio_ap_wakeup(shmd->gpio_ap_wakeup); + c2c_assign_gpio_ap_status(shmd->gpio_ap_status); + c2c_assign_gpio_cp_wakeup(shmd->gpio_cp_wakeup); + c2c_assign_gpio_cp_status(shmd->gpio_cp_status); + + gpio_set_value(shmd->gpio_pda_active, 1); + gpio_set_value(shmd->gpio_ap_status, 1); + + /* + ** Register interrupt handlers + */ + err = c2c_register_handler(c2c_irq_handler, shmd); + if (err) { + mif_err("%s: ERR! c2c_register_handler fail (err %d)\n", + ld->name, err); + goto error; + } + + snprintf(name, MIF_MAX_NAME_LEN, "%s_ap_wakeup", ld->name); + irq = shmd->irq_ap_wakeup; + irq_flags = (IRQF_NO_THREAD | IRQF_NO_SUSPEND | IRQF_TRIGGER_HIGH); + err = mif_register_isr(irq, ap_wakeup_handler, irq_flags, name, shmd); + if (err) + goto error; + + snprintf(name, MIF_MAX_NAME_LEN, "%s_cp_status", ld->name); + irq = shmd->irq_cp_status; + irq_flags = (IRQF_NO_THREAD | IRQF_NO_SUSPEND | IRQF_TRIGGER_HIGH); + err = mif_register_isr(irq, cp_status_handler, irq_flags, name, shmd); + if (err) + goto error; return ld; + +error: + mif_err("xxx\n"); + kfree(shmd); + return NULL; } + diff --git a/drivers/misc/modem_if/modem_link_device_c2c.h b/drivers/misc/modem_if/modem_link_device_c2c.h index 7ec9aa6..9dba90b 100644 --- a/drivers/misc/modem_if/modem_link_device_c2c.h +++ b/drivers/misc/modem_if/modem_link_device_c2c.h @@ -1,5 +1,4 @@ /* - * Copyright (C) 2010 Google, Inc. * Copyright (C) 2010 Samsung Electronics. * * This software is licensed under the terms of the GNU General Public @@ -8,209 +7,18 @@ * * 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 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * */ -#include <linux/wakelock.h> #ifndef __MODEM_LINK_DEVICE_C2C_H__ #define __MODEM_LINK_DEVICE_C2C_H__ -#define DPRAM_ERR_MSG_LEN 128 -#define DPRAM_ERR_DEVICE "c2cerr" +#include <mach/c2c.h> +#include "modem_link_device_shmem.h" -#define MAX_IDX 2 - -#define DPRAM_BASE_PTR 0x4000000 - -#define DPRAM_START_ADDRESS 0 -#define DPRAM_MAGIC_CODE_ADDRESS DPRAM_START_ADDRESS -#define DPRAM_GOTA_MAGIC_CODE_SIZE 0x4 -#define DPRAM_PDA2PHONE_FORMATTED_START_ADDRESS \ - (DPRAM_START_ADDRESS + DPRAM_GOTA_MAGIC_CODE_SIZE) -#define BSP_DPRAM_BASE_SIZE 0x1ff8 -#define DPRAM_END_OF_ADDRESS (BSP_DPRAM_BASE_SIZE - 1) -#define DPRAM_INTERRUPT_SIZE 0x2 -#define DPRAM_PDA2PHONE_INTERRUPT_ADDRESS \ - (DPRAM_START_ADDRESS + BSP_DPRAM_BASE_SIZE - DPRAM_INTERRUPT_SIZE*2) -#define DPRAM_PHONE2PDA_INTERRUPT_ADDRESS \ - (DPRAM_START_ADDRESS + BSP_DPRAM_BASE_SIZE) -#define DPRAM_BUFFER_SIZE \ - (DPRAM_PHONE2PDA_INTERRUPT_ADDRESS -\ - DPRAM_PDA2PHONE_FORMATTED_START_ADDRESS) -#define DPRAM_INDEX_SIZE 0x2 - -#define MAGIC_DMDL 0x4445444C -#define MAGIC_UMDL 0x4445444D - -#define DPRAM_PACKET_DATA_SIZE 0x3f00 -#define DPRAM_PACKET_HEADER_SIZE 0x7 - -#define INT_GOTA_MASK_VALID 0xA000 -#define INT_DPRAM_DUMP_MASK_VALID 0xA000 -#define MASK_CMD_RECEIVE_READY_NOTIFICATION 0xA100 -#define MASK_CMD_DOWNLOAD_START_REQUEST 0xA200 -#define MASK_CMD_DOWNLOAD_START_RESPONSE 0xA301 -#define MASK_CMD_IMAGE_SEND_REQUEST 0xA400 -#define MASK_CMD_IMAGE_SEND_RESPONSE 0xA500 -#define MASK_CMD_SEND_DONE_REQUEST 0xA600 -#define MASK_CMD_SEND_DONE_RESPONSE 0xA701 -#define MASK_CMD_STATUS_UPDATE_NOTIFICATION 0xA800 -#define MASK_CMD_UPDATE_DONE_NOTIFICATION 0xA900 -#define MASK_CMD_EFS_CLEAR_RESPONSE 0xAB00 -#define MASK_CMD_ALARM_BOOT_OK 0xAC00 -#define MASK_CMD_ALARM_BOOT_FAIL 0xAD00 - -#define WRITEIMG_HEADER_SIZE 8 -#define WRITEIMG_TAIL_SIZE 4 -#define WRITEIMG_BODY_SIZE \ - (DPRAM_BUFFER_SIZE - WRITEIMG_HEADER_SIZE - WRITEIMG_TAIL_SIZE) - -#define DPDN_DEFAULT_WRITE_LEN WRITEIMG_BODY_SIZE -#define CMD_DL_START_REQ 0x9200 -#define CMD_IMG_SEND_REQ 0x9400 -#define CMD_DL_SEND_DONE_REQ 0x9600 - -#define CMD_UL_START_REQ 0x9200 -#define CMD_UL_START_READY 0x9400 -#define CMD_UL_SEND_RESP 0x9601 -#define CMD_UL_SEND_DONE_RESP 0x9801 -#define CMD_UL_SEND_REQ 0xA500 -#define CMD_UL_START_RESPONSE 0xA301 -#define CMD_UL_SEND_DONE_REQ 0xA700 -#define CMD_RECEIVE_READY_NOTIFICATION 0xA100 - -#define MASK_CMD_RESULT_FAIL 0x0002 -#define MASK_CMD_RESULT_SUCCESS 0x0001 - -#define START_INDEX 0x007F -#define END_INDEX 0x007E - -#define CMD_IMG_SEND_REQ 0x9400 - -#define CRC_TAB_SIZE 256 -#define CRC_16_L_SEED 0xFFFF - -struct c2c_device { - /* DPRAM memory addresses */ - u16 *in_head_addr; - u16 *in_tail_addr; - u8 *in_buff_addr; - unsigned long in_buff_size; - - u16 *out_head_addr; - u16 *out_tail_addr; - u8 *out_buff_addr; - unsigned long out_buff_size; - - unsigned long in_head_saved; - unsigned long in_tail_saved; - unsigned long out_head_saved; - unsigned long out_tail_saved; - - u16 mask_req_ack; - u16 mask_res_ack; - u16 mask_send; -}; - -struct memory_region { - u8 *control; - u8 *fmt_out; - u8 *raw_out; - u8 *fmt_in; - u8 *raw_in; - u8 *mbx; -}; - -struct UldDataHeader { - u8 bop; - u16 total_frame; - u16 curr_frame; - u16 len; -}; - -struct c2c_link_device { - struct link_device ld; - - struct modem_data *pdata; - - /*only c2c*/ - struct wake_lock c2c_wake_lock; - atomic_t raw_txq_req_ack_rcvd; - atomic_t fmt_txq_req_ack_rcvd; - u8 net_stop_flag; - int phone_sync; - u8 phone_status; - - struct work_struct xmit_work_struct; - - struct workqueue_struct *gota_wq; - struct work_struct gota_cmd_work; - - struct c2c_device dev_map[MAX_IDX]; - - struct wake_lock dumplock; - - u8 c2c_read_data[131072]; - - int c2c_init_cmd_wait_condition; - wait_queue_head_t c2c_init_cmd_wait_q; - - int modem_pif_init_wait_condition; - wait_queue_head_t modem_pif_init_done_wait_q; - - struct completion gota_download_start_complete; - - int gota_send_done_cmd_wait_condition; - wait_queue_head_t gota_send_done_cmd_wait_q; - - int gota_update_done_cmd_wait_condition; - wait_queue_head_t gota_update_done_cmd_wait_q; - - int upload_send_req_wait_condition; - wait_queue_head_t upload_send_req_wait_q; - - int upload_send_done_wait_condition; - wait_queue_head_t upload_send_done_wait_q; - - int upload_start_req_wait_condition; - wait_queue_head_t upload_start_req_wait_q; - - int upload_packet_start_condition; - wait_queue_head_t upload_packet_start_wait_q; - - u16 gota_irq_handler_cmd; - - u16 c2c_dump_handler_cmd; - - int dump_region_number; - - unsigned int is_c2c_err ; - - int c2c_dump_start; - int gota_start; - - char c2c_err_buf[DPRAM_ERR_MSG_LEN]; - - struct fasync_struct *c2c_err_async_q; - - void (*clear_interrupt)(struct c2c_link_device *); - - struct memory_region m_region; - - unsigned long fmt_out_buff_size; - unsigned long raw_out_buff_size; - unsigned long fmt_in_buff_size; - unsigned long raw_in_buff_size; - - struct delayed_work delayed_tx; - struct sk_buff *delayed_skb; - u8 delayed_count; -}; - -/* converts from struct link_device* to struct xxx_link_device* */ -#define to_c2c_link_device(linkdev) \ - container_of(linkdev, struct c2c_link_device, ld) +#define CP_WAKEUP_HOLD_TIME 500 /* 500 ms */ #endif + diff --git a/drivers/misc/modem_if/modem_link_device_dpram.c b/drivers/misc/modem_if/modem_link_device_dpram.c index a650ed9..a87aff2 100644 --- a/drivers/misc/modem_if/modem_link_device_dpram.c +++ b/drivers/misc/modem_if/modem_link_device_dpram.c @@ -25,560 +25,385 @@ #include <linux/if_arp.h> #include <linux/platform_device.h> #include <linux/kallsyms.h> -#include <linux/platform_data/modem.h> +#include <linux/suspend.h> +#include <plat/gpio-cfg.h> +#include <mach/gpio.h> +#include "modem.h" #include "modem_prj.h" -#include "modem_link_device_dpram.h" #include "modem_utils.h" +#include "modem_link_device_dpram.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); -static void dpram_dump_memory(struct link_device *ld, char *buff); - -/* -** 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 void trigger_forced_cp_crash(struct dpram_link_device *dpld); -static inline struct dpram_rxb *rxbq_get_free_rxb(struct dpram_rxb_queue *rxbq) +/** + * set_circ_pointer + * @dpld: pointer to an instance of dpram_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * @dir: direction of communication (TX or RX) + * @ptr: type of the queue pointer (HEAD or TAIL) + * @addr: address of the queue pointer + * @val: value to be written to the queue pointer + * + * Writes a value to a pointer in a circular queue with verification. + */ +static inline void set_circ_pointer(struct dpram_link_device *dpld, int id, + int dir, int ptr, void __iomem *addr, u16 val) { - struct dpram_rxb *rxb = NULL; + struct link_device *ld = &dpld->ld; + int cnt = 0; + u16 saved = 0; - 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; - } + iowrite16(val, addr); - return rxb; -} + while (1) { + /* Check the value written to the address */ + saved = ioread16(addr); + if (likely(saved == val)) + break; -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); -} + cnt++; + mif_err("%s: ERR! %s_%s.%s saved(%d) != val(%d), count %d\n", + ld->name, get_dev_name(id), circ_dir(dir), + circ_ptr(ptr), saved, val, cnt); + if (cnt >= MAX_RETRY_CNT) { + trigger_forced_cp_crash(dpld); + break; + } -static inline struct dpram_rxb *rxbq_get_data_rxb(struct dpram_rxb_queue *rxbq) -{ - struct dpram_rxb *rxb = NULL; + udelay(100); - if (likely(!rxbq_empty(rxbq))) { - rxb = &rxbq->rxb[rxbq->out]; - rxbq->out++; - if (rxbq->out >= rxbq->size) - rxbq->out -= rxbq->size; + /* Write the value again */ + iowrite16(val, addr); } - - 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) +/** + * recv_int2ap + * @dpld: pointer to an instance of dpram_link_device structure + * + * Returns the value of the CP-to-AP interrupt register in a DPRAM. + */ +static inline u16 recv_int2ap(struct dpram_link_device *dpld) { - rxb->data = NULL; - rxb->len = 0; + return ioread16(dpld->mbx2ap); } -/* -** 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) +/** + * send_int2cp + * @dpld: pointer to an instance of dpram_link_device structure + * @mask: value to be written to the AP-to-CP interrupt register in a DPRAM + */ +static inline void send_int2cp(struct dpram_link_device *dpld, u16 mask) { - int ret; + struct idpram_pm_op *pm_op = dpld->pm_op; - ret = request_irq(irq, isr, flag, name, dpld); - if (ret) { - mif_info("%s: ERR! request_irq fail (err %d)\n", name, ret); - return ret; + if (pm_op && pm_op->int2cp_possible) { + if (!pm_op->int2cp_possible(dpld)) + return; } - 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 (likely(dpld->dpctl->clear_intr)) - dpld->dpctl->clear_intr(); + iowrite16(mask, dpld->mbx2cp); } -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) +/** + * read_int2cp + * @dpld: pointer to an instance of dpram_link_device structure + * + * Returns the value of the AP-to-CP interrupt register in a DPRAM. + */ +static inline u16 read_int2cp(struct dpram_link_device *dpld) { - if (likely(dpld->dpctl->send_intr)) - dpld->dpctl->send_intr(mask); - else - iowrite16(mask, dpld->mbx2cp); + return ioread16(dpld->mbx2cp); } +/** + * get_magic + * @dpld: pointer to an instance of dpram_link_device structure + * + * Returns the value of the "magic code" field in a DPRAM. + */ static inline u16 get_magic(struct dpram_link_device *dpld) { return ioread16(dpld->magic); } +/** + * set_magic + * @dpld: pointer to an instance of dpram_link_device structure + * @val: value to be written to the "magic code" field in a DPRAM + */ static inline void set_magic(struct dpram_link_device *dpld, u16 val) { iowrite16(val, dpld->magic); } +/** + * get_access + * @dpld: pointer to an instance of dpram_link_device structure + * + * Returns the value of the "access enable" field in a DPRAM. + */ static inline u16 get_access(struct dpram_link_device *dpld) { return ioread16(dpld->access); } +/** + * set_access + * @dpld: pointer to an instance of dpram_link_device structure + * @val: value to be written to the "access enable" field in a DPRAM + */ 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) +/** + * get_txq_head + * @dpld: pointer to an instance of dpram_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Returns the value of a head (in) pointer in a TX queue. + */ +static inline u32 get_txq_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) +/** + * get_txq_tail + * @dpld: pointer to an instance of dpram_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Returns the value of a tail (out) pointer in a TX queue. + * + * It is useless for an AP to read a tail pointer in a TX queue twice to verify + * whether or not the value in the pointer is valid, because it can already have + * been updated by a CP after the first access from the AP. + */ +static inline u32 get_txq_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) +/** + * get_txq_buff + * @dpld: pointer to an instance of dpram_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Returns the start address of the buffer in a TXQ. + */ +static inline u8 *get_txq_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) +/** + * get_txq_buff_size + * @dpld: pointer to an instance of dpram_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Returns the size of the buffer in a TXQ. + */ +static inline u32 get_txq_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) +/** + * get_rxq_head + * @dpld: pointer to an instance of dpram_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Returns the value of a head (in) pointer in an RX queue. + * + * It is useless for an AP to read a head pointer in an RX queue twice to verify + * whether or not the value in the pointer is valid, because it can already have + * been updated by a CP after the first access from the AP. + */ +static inline u32 get_rxq_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) +/** + * get_rxq_tail + * @dpld: pointer to an instance of dpram_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Returns the value of a tail (in) pointer in an RX queue. + */ +static inline u32 get_rxq_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) +/** + * get_rxq_buff + * @dpld: pointer to an instance of dpram_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Returns the start address of the buffer in an RXQ. + */ +static inline u8 *get_rxq_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) +/** + * get_rxq_buff_size + * @dpld: pointer to an instance of dpram_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Returns the size of the buffer in an RXQ. + */ +static inline u32 get_rxq_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) +/** + * set_txq_head + * @dpld: pointer to an instance of dpram_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * @in: value to be written to the head pointer in a TXQ + */ +static inline void set_txq_head(struct dpram_link_device *dpld, int id, u32 in) { - return dpld->dev[id]->mask_req_ack; + set_circ_pointer(dpld, id, TX, HEAD, dpld->dev[id]->txq.head, in); } -static inline u16 get_mask_res_ack(struct dpram_link_device *dpld, int id) +/** + * set_txq_tail + * @dpld: pointer to an instance of dpram_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * @out: value to be written to the tail pointer in a TXQ + */ +static inline void set_txq_tail(struct dpram_link_device *dpld, int id, u32 out) { - return dpld->dev[id]->mask_res_ack; + set_circ_pointer(dpld, id, TX, TAIL, dpld->dev[id]->txq.tail, out); } -static inline u16 get_mask_send(struct dpram_link_device *dpld, int id) +/** + * set_rxq_head + * @dpld: pointer to an instance of dpram_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * @in: value to be written to the head pointer in an RXQ + */ +static inline void set_rxq_head(struct dpram_link_device *dpld, int id, u32 in) { - return dpld->dev[id]->mask_send; + set_circ_pointer(dpld, id, RX, HEAD, dpld->dev[id]->rxq.head, in); } -static inline bool dpram_circ_valid(u32 size, u32 in, u32 out) +/** + * set_rxq_tail + * @dpld: pointer to an instance of dpram_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * @out: value to be written to the tail pointer in an RXQ + */ +static inline void set_rxq_tail(struct dpram_link_device *dpld, int id, u32 out) { - if (in >= size) - return false; - - if (out >= size) - return false; - - return true; + set_circ_pointer(dpld, id, RX, TAIL, dpld->dev[id]->rxq.tail, out); } -/* 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) +/** + * get_mask_req_ack + * @dpld: pointer to an instance of dpram_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Returns the REQ_ACK mask value for the IPC device. + */ +static inline u16 get_mask_req_ack(struct dpram_link_device *dpld, int id) { - set_tx_head(dpld, dev, 0); - set_tx_tail(dpld, dev, 0); - if (dev == IPC_FMT) - trigger_force_cp_crash(dpld); + return dpld->dev[id]->mask_req_ack; } -/* 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) +/** + * get_mask_res_ack + * @dpld: pointer to an instance of dpram_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Returns the RES_ACK mask value for the IPC device. + */ +static inline u16 get_mask_res_ack(struct dpram_link_device *dpld, int id) { - 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; + return dpld->dev[id]->mask_res_ack; } -static void dpram_reset_rx_circ(struct dpram_link_device *dpld, int dev) +/** + * get_mask_send + * @dpld: pointer to an instance of dpram_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Returns the SEND mask value for the IPC device. + */ +static inline u16 get_mask_send(struct dpram_link_device *dpld, int id) { - set_rx_head(dpld, dev, 0); - set_rx_tail(dpld, dev, 0); - if (dev == IPC_FMT) - trigger_force_cp_crash(dpld); + return dpld->dev[id]->mask_send; } -/* -** CAUTION : dpram_allow_sleep() MUST be invoked after dpram_wake_up() success -*/ -static int dpram_wake_up(struct dpram_link_device *dpld) +/** + * reset_txq_circ + * @dpld: pointer to an instance of dpram_link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Empties a TXQ by resetting the head (in) pointer with the value in the tail + * (out) pointer. + */ +static inline void reset_txq_circ(struct dpram_link_device *dpld, int dev) { struct link_device *ld = &dpld->ld; + u32 head = get_txq_head(dpld, dev); + u32 tail = get_txq_tail(dpld, dev); - if (!dpld->dpctl->wakeup) - return 0; - - if (dpld->dpctl->wakeup() < 0) { - mif_err("%s: ERR! <%pf> DPRAM wakeup fail once\n", - ld->name, __builtin_return_address(0)); - - if (dpld->dpctl->sleep) - dpld->dpctl->sleep(); + mif_info("%s: %s_TXQ: HEAD[%u] <== TAIL[%u]\n", + ld->name, get_dev_name(dev), head, tail); - 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; + set_txq_head(dpld, dev, tail); } -static void dpram_allow_sleep(struct dpram_link_device *dpld) +/** + * reset_rxq_circ + * @dpld: pointer to an instance of dpram_link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Empties an RXQ by resetting the tail (out) pointer with the value in the head + * (in) pointer. + */ +static inline void reset_rxq_circ(struct dpram_link_device *dpld, int dev) { struct link_device *ld = &dpld->ld; + u32 head = get_rxq_head(dpld, dev); + u32 tail = get_rxq_tail(dpld, dev); - if (!dpld->dpctl->sleep) - return; + mif_info("%s: %s_RXQ: TAIL[%u] <== HEAD[%u]\n", + ld->name, get_dev_name(dev), tail, head); - if (atomic_dec_return(&dpld->accessing) <= 0) { - dpld->dpctl->sleep(); - atomic_set(&dpld->accessing, 0); - mif_debug("%s: DPRAM sleep possible\n", ld->name); - } + set_rxq_tail(dpld, dev, head); } -static int dpram_check_access(struct dpram_link_device *dpld) +/** + * check_magic_access + * @dpld: pointer to an instance of dpram_link_device structure + * + * Returns 0 if the "magic code" and "access enable" values are valid, otherwise + * returns -EACCES. + */ +static int check_magic_access(struct dpram_link_device *dpld) { struct link_device *ld = &dpld->ld; int i; u16 magic = get_magic(dpld); u16 access = get_access(dpld); + /* Returns 0 if the "magic code" and "access enable" are valid */ if (likely(magic == DPRAM_MAGIC_CODE && access == 1)) return 0; + /* Retry up to 100 times with 100 us delay per each retry */ for (i = 1; i <= 100; i++) { - mif_info("%s: ERR! magic:%X access:%X -> retry:%d\n", + mif_info("%s: magic:%X access:%X -> retry:%d\n", ld->name, magic, access, i); udelay(100); @@ -592,357 +417,313 @@ static int dpram_check_access(struct dpram_link_device *dpld) return -EACCES; } -static bool dpram_ipc_active(struct dpram_link_device *dpld) +/** + * ipc_active + * @dpld: pointer to an instance of dpram_link_device structure + * + * Returns whether or not IPC via the dpram_link_device instance is possible. + */ +static bool 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)); + mif_err("%s: <called by %pf> ERR! ld->mode != LINK_MODE_IPC\n", + ld->name, CALLER); return false; } - if (dpram_check_access(dpld) < 0) { - mif_info("%s: ERR! <%pf> dpram_check_access fail\n", - ld->name, __builtin_return_address(0)); + /* Check "magic code" and "access enable" values */ + if (check_magic_access(dpld) < 0) { + mif_err("%s: <called by %pf> ERR! check_magic_access fail\n", + ld->name, CALLER); 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 ++++++++++ */ - memcpy((buff + in), src, len); - } else { - /* ------ out +++++++++++ in ------------ */ - u32 space = qsize - in; - - /* 1) in -> buffer end */ - memcpy((buff + in), src, ((len > space) ? space : len)); - - /* 2) buffer start -> out */ - if (len > space) - memcpy(buff, (src + space), (len - space)); - } - - /* update new in pointer */ - inp = in + len; - if (inp >= qsize) - inp -= qsize; - set_tx_head(dpld, dev, inp); +/** + * get_dpram_status + * @dpld: pointer to an instance of dpram_link_device structure + * @dir: direction of communication (TX or RX) + * @stat: pointer to an instance of mem_status structure + * + * Takes a snapshot of the current status of a DPRAM. + */ +static void get_dpram_status(struct dpram_link_device *dpld, + enum circ_dir_type dir, struct mem_status *stat) +{ +#ifdef DEBUG_MODEM_IF + getnstimeofday(&stat->ts); +#endif + + stat->dir = dir; + stat->magic = get_magic(dpld); + stat->access = get_access(dpld); + stat->head[IPC_FMT][TX] = get_txq_head(dpld, IPC_FMT); + stat->tail[IPC_FMT][TX] = get_txq_tail(dpld, IPC_FMT); + stat->head[IPC_FMT][RX] = get_rxq_head(dpld, IPC_FMT); + stat->tail[IPC_FMT][RX] = get_rxq_tail(dpld, IPC_FMT); + stat->head[IPC_RAW][TX] = get_txq_head(dpld, IPC_RAW); + stat->tail[IPC_RAW][TX] = get_txq_tail(dpld, IPC_RAW); + stat->head[IPC_RAW][RX] = get_rxq_head(dpld, IPC_RAW); + stat->tail[IPC_RAW][RX] = get_rxq_tail(dpld, IPC_RAW); + stat->int2ap = recv_int2ap(dpld); + stat->int2cp = read_int2cp(dpld); +} + +#if 0 +/** + * save_ipc_trace_work + * @work: pointer to an instance of work_struct structure + * + * Performs actual file operation for saving RX IPC trace. + */ +static void save_ipc_trace_work(struct work_struct *work) +{ + struct dpram_link_device *dpld; + struct link_device *ld; + struct trace_data_queue *trq; + struct trace_data *trd; + struct circ_status *stat; + struct file *fp; + struct timespec *ts; + int dev; + u8 *dump; + int rcvd; + u8 *buff; + char *path; + struct utc_time utc; - 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); - } + dpld = container_of(work, struct dpram_link_device, trace_dwork.work); + ld = &dpld->ld; + trq = &dpld->trace_list; + path = dpld->trace_path; - 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); + buff = kzalloc(dpld->size << 3, GFP_KERNEL); + if (!buff) { + while (1) { + trd = trq_get_data_slot(trq); + if (!trd) + break; + + ts = &trd->ts; + dev = trd->dev; + stat = &trd->circ_stat; + dump = trd->data; + rcvd = trd->size; + print_ipc_trace(ld, dev, stat, ts, dump, rcvd); + + kfree(dump); + } + return; } -} - -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_lock[dev], flags); while (1) { - space = dpram_get_txq_space(dpld, dev, qsize, &in, &out); - if (unlikely(space < 0)) { - spin_unlock_irqrestore(&dpld->tx_lock[dev], flags); - dpram_reset_tx_circ(dpld, dev); - return space; - } - - skb = skb_dequeue(txq); - if (unlikely(!skb)) + trd = trq_get_data_slot(trq); + if (!trd) break; - if (unlikely(space < skb->len)) { - atomic_set(&dpld->res_required[dev], 1); - skb_queue_head(txq, skb); - 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); - return -ENOSPC; + ts = &trd->ts; + dev = trd->dev; + stat = &trd->circ_stat; + dump = trd->data; + rcvd = trd->size; + + ts2utc(ts, &utc); + snprintf(path, MIF_MAX_PATH_LEN, + "%s/%s_%s_%d%02d%02d-%02d%02d%02d.lst", + MIF_LOG_DIR, ld->name, get_dev_name(dev), + utc.year, utc.mon, utc.day, utc.hour, utc.min, utc.sec); + + fp = mif_open_file(path); + if (fp) { + int len; + + snprintf(buff, MIF_MAX_PATH_LEN, + "[%d-%02d-%02d %02d:%02d:%02d.%03d] " + "%s %s_RXQ {IN:%u OUT:%u LEN:%d}\n", + utc.year, utc.mon, utc.day, utc.hour, utc.min, + utc.sec, utc.msec, ld->name, get_dev_name(dev), + stat->in, stat->out, stat->size); + len = strlen(buff); + mif_dump2format4(dump, rcvd, (buff + len), NULL); + strcat(buff, "\n"); + len = strlen(buff); + + mif_save_file(fp, buff, len); + + memset(buff, 0, len); + mif_close_file(fp); + } else { + mif_err("%s: %s open fail\n", ld->name, path); + print_ipc_trace(ld, dev, stat, ts, dump, rcvd); } - /* 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); + kfree(dump); } - spin_unlock_irqrestore(&dpld->tx_lock[dev], 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--; - } - } + kfree(buff); } +#endif -static void dpram_ipc_read(struct dpram_link_device *dpld, int dev, u8 *dst, - u8 __iomem *src, u32 out, u32 len, u32 qsize) +/** + * set_dpram_map + * @dpld: pointer to an instance of dpram_link_device structure + * @map: pointer to an instance of mif_irq_map structure + * + * Sets variables in an mif_irq_map instance as current DPRAM status for IPC + * logging. + */ +static void set_dpram_map(struct dpram_link_device *dpld, + struct mif_irq_map *map) { - if ((out + len) <= qsize) { - /* ----- (out) (in) ----- */ - /* ----- 7f 00 00 7e ----- */ - memcpy(dst, (src + out), len); - } else { - /* (in) ----------- (out) */ - /* 00 7e ----------- 7f 00 */ - unsigned len1 = qsize - out; + map->magic = get_magic(dpld); + map->access = get_access(dpld); - /* 1) out -> buffer end */ - memcpy(dst, (src + out), len1); + map->fmt_tx_in = get_txq_head(dpld, IPC_FMT); + map->fmt_tx_out = get_txq_tail(dpld, IPC_FMT); + map->fmt_rx_in = get_rxq_head(dpld, IPC_FMT); + map->fmt_rx_out = get_rxq_tail(dpld, IPC_FMT); + map->raw_tx_in = get_txq_head(dpld, IPC_RAW); + map->raw_tx_out = get_txq_tail(dpld, IPC_RAW); + map->raw_rx_in = get_rxq_head(dpld, IPC_RAW); + map->raw_rx_out = get_rxq_tail(dpld, IPC_RAW); - /* 2) buffer start -> in */ - dst += len1; - memcpy(dst, src, (len - len1)); - } + map->cp2ap = recv_int2ap(dpld); } -/* - 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) +/** + * dpram_wake_up + * @dpld: pointer to an instance of dpram_link_device structure + * + * Wakes up a DPRAM if it can sleep and increases the "accessing" counter in the + * dpram_link_device instance. + * + * CAUTION!!! dpram_allow_sleep() MUST be invoked after dpram_wake_up() success + * to decrease the "accessing" counter. + */ +static int dpram_wake_up(struct dpram_link_device *dpld) { 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; - - 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")); - } + if (unlikely(!dpld->need_wake_up)) + return 0; - /* 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)); - return -ENOMEM; + if (dpld->ext_op->wakeup(dpld) < 0) { + mif_err("%s: <called by %pf> ERR! wakeup fail\n", + ld->name, CALLER); + return -EACCES; } - /* 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); + atomic_inc(&dpld->accessing); - return rcvd; + return 0; } -/* - 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) +/** + * dpram_allow_sleep + * @dpld: pointer to an instance of dpram_link_device structure + * + * Decreases the "accessing" counter in the dpram_link_device instance if it can + * sleep and allows the DPRAM to sleep only if the value of "accessing" counter + * is less than or equal to 0. + * + * MUST be invoked after dpram_wake_up() success to decrease the "accessing" + * counter. + */ +static void dpram_allow_sleep(struct dpram_link_device *dpld) { 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); + if (unlikely(!dpld->need_wake_up)) + return; - /* Calculate and set new out */ - rest -= tot; - out += tot; - if (out >= qsize) - out -= qsize; + if (atomic_dec_return(&dpld->accessing) <= 0) { + dpld->ext_op->sleep(dpld); + atomic_set(&dpld->accessing, 0); + mif_debug("%s: DPRAM sleep possible\n", ld->name); } - - set_rx_tail(dpld, dev, out); - - return rcvd; } -static void non_command_handler(struct dpram_link_device *dpld, u16 intr) +static int capture_dpram_snapshot(struct link_device *ld, struct io_device *iod) { - struct link_device *ld = &dpld->ld; - int i = 0; - int ret = 0; - u16 tx_mask = 0; + struct dpram_link_device *dpld = to_dpram_link_device(ld); + struct sk_buff *skb; + u32 size = dpld->size; + u32 copied = 0; + u8 *dump; - if (!dpram_ipc_active(dpld)) - return; + dpram_wake_up(dpld); + dump = capture_mem_dump(ld, dpld->base, dpld->size); + dpram_allow_sleep(dpld); - /* 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); + if (!dump) + return -ENOMEM; - /* Check and process REQ_ACK (at this time, in == out) */ - if (intr & 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); + while (copied < size) { + skb = alloc_skb(MAX_DUMP_SKB_SIZE, GFP_ATOMIC); + if (!skb) { + mif_err("ERR! alloc_skb fail\n"); + kfree(dump); + return -ENOMEM; } - } - if (!dpld->use_skb) { - /* Schedule soft IRQ for RX */ - tasklet_hi_schedule(&dpld->rx_tsk); - } + skb_put(skb, MAX_DUMP_SKB_SIZE); + memcpy(skb->data, (dump + copied), MAX_DUMP_SKB_SIZE); + copied += MAX_DUMP_SKB_SIZE; - /* 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); - } - } + skb_queue_tail(&iod->sk_rx_q, skb); + wake_up(&iod->wq); } - if (tx_mask) { - send_intr(dpld, INT_NON_CMD(tx_mask)); - mif_debug("%s: send intr 0x%04X\n", ld->name, tx_mask); - } + kfree(dump); + return 0; } +/** + * handle_cp_crash + * @dpld: pointer to an instance of dpram_link_device structure + * + * Actual handler for the CRASH_EXIT command from a CP. + */ 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)); + if (dpld->forced_cp_crash) + dpld->forced_cp_crash = false; + + /* Stop network interfaces */ + mif_netif_stop(ld); + + /* Purge the skb_txq in every IPC device (IPC_FMT, IPC_RAW, etc.) */ + for (i = 0; i < ld->max_ipc_dev; i++) skb_queue_purge(ld->skb_txq[i]); - } + /* Change the modem state to STATE_CRASH_EXIT for the FMT IO device */ iod = link_get_iod_with_format(ld, IPC_FMT); - iod->modem_state_changed(iod, STATE_CRASH_EXIT); + if (iod) + iod->modem_state_changed(iod, STATE_CRASH_EXIT); + /* Change the modem state to STATE_CRASH_EXIT for the BOOT IO device */ 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); + iod->modem_state_changed(iod, STATE_CRASH_EXIT); } -static void handle_no_crash_ack(unsigned long arg) +/** + * handle_no_cp_crash_ack + * @arg: pointer to an instance of dpram_link_device structure + * + * Invokes handle_cp_crash() to enter the CRASH_EXIT state if there was no + * CRASH_ACK from a CP in FORCE_CRASH_ACK_TIMEOUT. + */ +static void handle_no_cp_crash_ack(unsigned long arg) { struct dpram_link_device *dpld = (struct dpram_link_device *)arg; struct link_device *ld = &dpld->ld; @@ -955,165 +736,341 @@ static void handle_no_crash_ack(unsigned long arg) handle_cp_crash(dpld); } -static int trigger_force_cp_crash(struct dpram_link_device *dpld) +/** + * trigger_forced_cp_crash + * @dpld: pointer to an instance of dpram_link_device structure + * + * Triggers an enforced CP crash. + */ +static void trigger_forced_cp_crash(struct dpram_link_device *dpld) { struct link_device *ld = &dpld->ld; +#ifdef DEBUG_MODEM_IF + struct trace_data *trd; + u8 *dump; + struct timespec ts; + getnstimeofday(&ts); +#endif if (ld->mode == LINK_MODE_ULOAD) { - mif_err("%s: CP crash is already in progress\n", ld->mc->name); - return 0; + mif_err("%s: <called by %pf> ALREADY in progress\n", + ld->name, CALLER); + return; } ld->mode = LINK_MODE_ULOAD; - mif_err("%s: called by %pf\n", ld->name, __builtin_return_address(0)); + dpld->forced_cp_crash = true; + + disable_irq_nosync(dpld->irq); + + dpram_wake_up(dpld); + +#ifdef DEBUG_MODEM_IF + dump = capture_mem_dump(ld, dpld->base, dpld->size); + if (dump) { + trd = trq_get_free_slot(&dpld->trace_list); + memcpy(&trd->ts, &ts, sizeof(struct timespec)); + trd->dev = IPC_DEBUG; + trd->data = dump; + trd->size = dpld->size; + } +#endif - if (dpld->dp_type == CP_IDPRAM) - dpram_wake_up(dpld); + enable_irq(dpld->irq); - send_intr(dpld, INT_CMD(INT_CMD_CRASH_EXIT)); + mif_err("%s: <called by %pf>\n", ld->name, CALLER); + /* Send CRASH_EXIT command to a CP */ + send_int2cp(dpld, INT_CMD(INT_CMD_CRASH_EXIT)); + get_dpram_status(dpld, TX, msq_get_free_slot(&dpld->stat_list)); + + /* If there is no CRASH_ACK from a CP in FORCE_CRASH_ACK_TIMEOUT, + handle_no_cp_crash_ack() will be executed. */ mif_add_timer(&dpld->crash_ack_timer, FORCE_CRASH_ACK_TIMEOUT, - handle_no_crash_ack, (unsigned long)dpld); + handle_no_cp_crash_ack, (unsigned long)dpld); - return 0; + return; } -static int dpram_init_ipc(struct dpram_link_device *dpld) +/** + * ext_command_handler + * @dpld: pointer to an instance of dpram_link_device structure + * @cmd: extended DPRAM command from a CP + * + * Processes an extended command from a CP. + */ +static void ext_command_handler(struct dpram_link_device *dpld, u16 cmd) { 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); + u16 resp; - /* 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); - } + switch (EXT_CMD_MASK(cmd)) { + case EXT_CMD_SET_SPEED_LOW: + if (dpld->dpram->setup_speed) { + dpld->dpram->setup_speed(DPRAM_SPEED_LOW); + resp = INT_EXT_CMD(EXT_CMD_SET_SPEED_LOW); + send_int2cp(dpld, resp); + } + break; - /* 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); + case EXT_CMD_SET_SPEED_MID: + if (dpld->dpram->setup_speed) { + dpld->dpram->setup_speed(DPRAM_SPEED_MID); + resp = INT_EXT_CMD(EXT_CMD_SET_SPEED_MID); + send_int2cp(dpld, resp); + } + break; - if (dpld->iod[IPC_RAW]->recv_skb) - dpld->use_skb = true; + case EXT_CMD_SET_SPEED_HIGH: + if (dpld->dpram->setup_speed) { + dpld->dpram->setup_speed(DPRAM_SPEED_HIGH); + resp = INT_EXT_CMD(EXT_CMD_SET_SPEED_HIGH); + send_int2cp(dpld, resp); + } + break; - 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]); + default: + mif_info("%s: unknown command 0x%04X\n", ld->name, cmd); + break; } +} - /* 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; +/** + * udl_command_handler + * @dpld: pointer to an instance of dpram_link_device structure + * @cmd: DPRAM upload/download command from a CP + * + * Processes a command for upload/download from a CP. + */ +static void udl_command_handler(struct dpram_link_device *dpld, u16 cmd) +{ + struct link_device *ld = &dpld->ld; - ld->mode = LINK_MODE_IPC; + if (cmd & UDL_RESULT_FAIL) { + mif_err("%s: ERR! command fail (0x%04X)\n", ld->name, cmd); + return; + } - if (wake_lock_active(&dpld->wlock)) - wake_unlock(&dpld->wlock); + switch (UDL_CMD_MASK(cmd)) { + case UDL_CMD_RECV_READY: + mif_err("%s: [CP->AP] CMD_DL_READY (0x%04X)\n", ld->name, cmd); +#ifdef CONFIG_CDMA_MODEM_CBP72 + mif_err("%s: [AP->CP] CMD_DL_START_REQ (0x%04X)\n", + ld->name, CMD_DL_START_REQ); + send_int2cp(dpld, CMD_DL_START_REQ); +#else + complete(&dpld->udl_cmpl); +#endif + break; - return 0; + default: + complete(&dpld->udl_cmpl); + } } +/** + * cmd_req_active_handler + * @dpld: pointer to an instance of dpram_link_device structure + * + * Handles the REQ_ACTIVE command from a CP. + */ static void cmd_req_active_handler(struct dpram_link_device *dpld) { - send_intr(dpld, INT_CMD(INT_CMD_RES_ACTIVE)); + send_int2cp(dpld, INT_CMD(INT_CMD_RES_ACTIVE)); } +/** + * cmd_crash_reset_handler + * @dpld: pointer to an instance of dpram_link_device structure + * + * Handles the CRASH_RESET command from a CP. + */ static void cmd_crash_reset_handler(struct dpram_link_device *dpld) { struct link_device *ld = &dpld->ld; struct io_device *iod = NULL; + int i; ld->mode = LINK_MODE_ULOAD; if (!wake_lock_active(&dpld->wlock)) wake_lock(&dpld->wlock); + /* Stop network interfaces */ + mif_netif_stop(ld); + + /* Purge the skb_txq in every IPC device (IPC_FMT, IPC_RAW, etc.) */ + for (i = 0; i < ld->max_ipc_dev; i++) + skb_queue_purge(ld->skb_txq[i]); + mif_err("%s: Recv 0xC7 (CRASH_RESET)\n", ld->name); + /* Change the modem state to STATE_CRASH_RESET for the FMT IO device */ iod = link_get_iod_with_format(ld, IPC_FMT); iod->modem_state_changed(iod, STATE_CRASH_RESET); + /* Change the modem state to STATE_CRASH_RESET for the BOOT IO device */ iod = link_get_iod_with_format(ld, IPC_BOOT); iod->modem_state_changed(iod, STATE_CRASH_RESET); } +/** + * cmd_crash_exit_handler + * @dpld: pointer to an instance of dpram_link_device structure + * + * Handles the CRASH_EXIT command from a CP. + */ static void cmd_crash_exit_handler(struct dpram_link_device *dpld) { struct link_device *ld = &dpld->ld; - u32 size = dpld->dpctl->dp_size; - char *dpram_buff = NULL; +#ifdef DEBUG_MODEM_IF + struct trace_data *trd; + u8 *dump; + struct timespec ts; + getnstimeofday(&ts); +#endif 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); - - if (dpld->dp_type == CP_IDPRAM) - dpram_wake_up(dpld); + del_timer(&dpld->crash_ack_timer); - 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_wake_up(dpld); + +#ifdef DEBUG_MODEM_IF + if (!dpld->forced_cp_crash) { + dump = capture_mem_dump(ld, dpld->base, dpld->size); + if (dump) { + trd = trq_get_free_slot(&dpld->trace_list); + memcpy(&trd->ts, &ts, sizeof(struct timespec)); + trd->dev = IPC_DEBUG; + trd->data = dump; + trd->size = dpld->size; + } } - - del_timer(&dpld->crash_ack_timer); +#endif if (dpld->ext_op && dpld->ext_op->crash_log) dpld->ext_op->crash_log(dpld); + mif_err("%s: Recv 0xC9 (CRASH_EXIT)\n", ld->name); + handle_cp_crash(dpld); } -static void cmd_phone_start_handler(struct dpram_link_device *dpld) +/** + * init_dpram_ipc + * @dpld: pointer to an instance of dpram_link_device structure + * + * Initializes IPC via DPRAM. + */ +static int init_dpram_ipc(struct dpram_link_device *dpld) { struct link_device *ld = &dpld->ld; - struct io_device *iod = NULL; + 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 < ld->max_ipc_dev; i++) { + set_txq_head(dpld, i, 0); + set_txq_tail(dpld, i, 0); + set_rxq_head(dpld, i, 0); + set_rxq_tail(dpld, i, 0); + } + + /* Initialize variables for efficient TX/RX processing */ + for (i = 0; i < ld->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); + + /* Initialize variables for TX flow control */ + for (i = 0; i < ld->max_ipc_dev; i++) + atomic_set(&dpld->res_required[i], 0); + + /* Enable IPC */ + if (wake_lock_active(&dpld->wlock)) + wake_unlock(&dpld->wlock); + + 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; + + return 0; +} + +/** + * reset_dpram_ipc + * @dpld: pointer to an instance of dpram_link_device structure + * + * Reset DPRAM with IPC map. + */ +static void reset_dpram_ipc(struct dpram_link_device *dpld) +{ + int i; + struct link_device *ld = &dpld->ld; + + dpld->set_access(dpld, 0); + + /* Clear pointers in every circular queue */ + for (i = 0; i < ld->max_ipc_dev; i++) { + dpld->set_txq_head(dpld, i, 0); + dpld->set_txq_tail(dpld, i, 0); + dpld->set_rxq_head(dpld, i, 0); + dpld->set_rxq_tail(dpld, i, 0); + } - mif_info("%s: Recv 0xC8 (CP_START)\n", ld->name); + dpld->set_magic(dpld, DPRAM_MAGIC_CODE); + dpld->set_access(dpld, 1); +} + +/** + * cmd_phone_start_handler + * @dpld: pointer to an instance of dpram_link_device structure + * + * Handles the PHONE_START command from a CP. + */ +static void cmd_phone_start_handler(struct dpram_link_device *dpld) +{ + struct link_device *ld = &dpld->ld; + struct io_device *iod; - dpram_init_ipc(dpld); + mif_err("%s: Recv 0xC8 (CP_START)\n", ld->name); iod = link_get_iod_with_format(ld, IPC_FMT); if (!iod) { - mif_info("%s: ERR! no iod\n", ld->name); + mif_err("%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); + init_dpram_ipc(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); - } + 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)); + if (dpld->ext_op && dpld->ext_op->cp_start_handler) { + dpld->ext_op->cp_start_handler(dpld); + } else { + mif_err("%s: Send 0xC2 (INIT_END)\n", ld->name); + send_int2cp(dpld, INT_CMD(INT_CMD_INIT_END)); + } } -static void command_handler(struct dpram_link_device *dpld, u16 cmd) +/** + * cmd_handler: processes a DPRAM command from a CP + * @dpld: pointer to an instance of dpram_link_device structure + * @cmd: DPRAM command from a CP + */ +static void cmd_handler(struct dpram_link_device *dpld, u16 cmd) { struct link_device *ld = &dpld->ld; @@ -1123,19 +1080,19 @@ static void command_handler(struct dpram_link_device *dpld, u16 cmd) break; case INT_CMD_CRASH_RESET: - dpld->dpram_init_status = DPRAM_INIT_STATE_NONE; + dpld->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; + dpld->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; + dpld->init_status = DPRAM_INIT_STATE_READY; cmd_phone_start_handler(dpld); - complete_all(&dpld->dpram_init_cmd); + complete_all(&ld->init_cmpl); break; case INT_CMD_NV_REBUILDING: @@ -1143,14 +1100,14 @@ static void command_handler(struct dpram_link_device *dpld, u16 cmd) break; case INT_CMD_PIF_INIT_DONE: - complete_all(&dpld->modem_pif_init_done); + complete_all(&ld->pif_cmpl); break; case INT_CMD_SILENT_NV_REBUILDING: mif_info("%s: SILENT_NV_REBUILDING\n", ld->name); break; - case INT_CMD_NORMAL_PWR_OFF: + case INT_CMD_NORMAL_POWER_OFF: /*ToDo:*/ /*kernel_sec_set_cp_ack()*/; break; @@ -1165,72 +1122,437 @@ static void command_handler(struct dpram_link_device *dpld, u16 cmd) } } -static void ext_command_handler(struct dpram_link_device *dpld, u16 cmd) +/** + * ipc_rx_work + * @work: pointer to an instance of the work_struct structure + * + * Invokes the recv method in the io_device instance to perform receiving IPC + * messages from each skb. + */ +static void ipc_rx_work(struct work_struct *work) +{ + struct dpram_link_device *dpld; + struct link_device *ld; + struct io_device *iod; + struct sk_buff *skb; + int i; + + dpld = container_of(work, struct dpram_link_device, rx_dwork.work); + ld = &dpld->ld; + + for (i = 0; i < ld->max_ipc_dev; i++) { + iod = dpld->iod[i]; + while (1) { + skb = skb_dequeue(ld->skb_rxq[i]); + if (!skb) + break; + + if (iod->recv_skb) { + iod->recv_skb(iod, ld, skb); + } else { + iod->recv(iod, ld, skb->data, skb->len); + dev_kfree_skb_any(skb); + } + } + } +} + +/** + * get_rxq_rcvd + * @dpld: pointer to an instance of dpram_link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * @mst: pointer to an instance of mem_status structure + * OUT @dcst: pointer to an instance of circ_status structure + * + * Stores {start address of the buffer in a RXQ, size of the buffer, in & out + * pointer values, size of received data} into the 'stat' instance. + * + * Returns an error code. + */ +static int get_rxq_rcvd(struct dpram_link_device *dpld, int dev, + struct mem_status *mst, struct circ_status *dcst) { 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); + dcst->buff = get_rxq_buff(dpld, dev); + dcst->qsize = get_rxq_buff_size(dpld, dev); + dcst->in = mst->head[dev][RX]; + dcst->out = mst->tail[dev][RX]; + dcst->size = circ_get_usage(dcst->qsize, dcst->in, dcst->out); + + if (circ_valid(dcst->qsize, dcst->in, dcst->out)) { + mif_debug("%s: %s_RXQ qsize[%u] in[%u] out[%u] rcvd[%u]\n", + ld->name, get_dev_name(dev), dcst->qsize, dcst->in, + dcst->out, dcst->size); + return 0; + } else { + mif_err("%s: ERR! %s_RXQ invalid (qsize[%d] in[%d] out[%d])\n", + ld->name, get_dev_name(dev), dcst->qsize, dcst->in, + dcst->out); + return -EIO; + } +} + +/** + * rx_sipc4_frames + * @dpld: pointer to an instance of dpram_link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * @mst: pointer to an instance of mem_status structure + * + * Returns + * ret < 0 : error + * ret == 0 : ILLEGAL status + * ret > 0 : valid data + * + * Must be invoked only when there is data in the corresponding RXQ. + * + * Requires a bottom half (e.g. ipc_rx_task) that will invoke the recv method in + * the io_device instance. + */ +static int rx_sipc4_frames(struct dpram_link_device *dpld, int dev, + struct mem_status *mst) +{ + struct link_device *ld = &dpld->ld; + struct sk_buff *skb; + u8 *dst; + struct circ_status dcst; + int rcvd; + + rcvd = get_rxq_rcvd(dpld, dev, mst, &dcst); + if (unlikely(rcvd < 0)) { +#ifdef DEBUG_MODEM_IF + trigger_forced_cp_crash(dpld); +#endif + goto exit; + } + rcvd = dcst.size; + + /* Allocate an skb */ + skb = dev_alloc_skb(rcvd); + if (!skb) { + mif_info("%s: ERR! %s dev_alloc_skb fail\n", + ld->name, get_dev_name(dev)); + rcvd = -ENOMEM; + goto exit; + } + + /* Read data from the RXQ */ + dst = skb_put(skb, rcvd); + circ_read16_from_io(dst, dcst.buff, dcst.qsize, dcst.out, rcvd); + + /* Store the skb to the corresponding skb_rxq */ + skb_queue_tail(ld->skb_rxq[dev], skb); + +exit: + /* Update tail (out) pointer to empty out the RXQ */ + set_rxq_tail(dpld, dev, dcst.in); + + return rcvd; +} + +/** + * rx_sipc5_frames + * @dpld: pointer to an instance of dpram_link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * @mst: pointer to an instance of mem_status structure + * + * Returns + * ret < 0 : error + * ret == 0 : ILLEGAL status + * ret > 0 : valid data + * + * Must be invoked only when there is data in the corresponding RXQ. + * + * Requires a recv_skb method in the io_device instance, so this function must + * be used for only SIPC5. + */ +static int rx_sipc5_frames(struct dpram_link_device *dpld, int dev, + struct mem_status *mst) +{ + struct link_device *ld = &dpld->ld; + struct sk_buff *skb; + /** + * variables for the status of the circular queue + */ + u8 __iomem *src; + u8 hdr[SIPC5_MIN_HEADER_SIZE]; + struct circ_status dcst; + /** + * variables for RX processing + */ + int qsize; /* size of the queue */ + int rcvd; /* size of data in the RXQ or error */ + int rest; /* size of the rest data */ + int idx; /* index to the start of current frame */ + u8 *frm; /* pointer to current frame */ + u8 *dst; /* pointer to the destination buffer */ + int tot; /* total length including padding data */ + /** + * variables for debug logging + */ + struct mif_irq_map map; + + /* Get data size in the RXQ and in/out pointer values */ + rcvd = get_rxq_rcvd(dpld, dev, mst, &dcst); + if (unlikely(rcvd < 0)) { + mif_err("%s: ERR! rcvd %d < 0\n", ld->name, rcvd); + goto exit; + } + + rcvd = dcst.size; + src = dcst.buff; + qsize = dcst.qsize; + idx = dcst.out; + + if (dev == IPC_FMT) { + set_dpram_map(dpld, &map); + mif_irq_log(ld->mc->msd, map, "ipc_recv", sizeof("ipc_recv")); + } + +#if 0 + skb = dev_alloc_skb(rcvd); + + /* + ** If there is enough free space for an skb to store received + ** data at once, + */ + if (skb) { + /* Read all data from the RXQ to the skb */ + dst = skb_put(skb, rcvd); + if (unlikely(dpld->strict_io_access)) + circ_read16_from_io(dst, src, qsize, idx, rcvd); + else + circ_read(dst, src, qsize, idx, rcvd); + +#ifdef DEBUG_MODEM_IF + /* Verify data copied to the skb */ + if (ld->aligned && memcmp16_to_io((src + idx), dst, 4)) { + mif_err("%s: memcmp16_to_io fail\n", ld->name); + rcvd = -EIO; + goto exit; } - break; +#endif - 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); + /* Store the skb to the corresponding skb_rxq */ + skb_queue_tail(ld->skb_rxq[dev], skb); + + goto exit; + } + + /* + ** If there was no enough space to store received data at once, + */ +#endif + + rest = rcvd; + while (rest > 0) { + /* Calculate the start of an SIPC5 frame */ + frm = src + idx; + + /* Copy the header in the frame to the header buffer */ + if (unlikely(dpld->strict_io_access)) + memcpy16_from_io(hdr, frm, SIPC5_MIN_HEADER_SIZE); + else + memcpy(hdr, frm, SIPC5_MIN_HEADER_SIZE); + + /* Check the config field in the header */ + if (unlikely(!sipc5_start_valid(hdr))) { + char str[MIF_MAX_STR_LEN]; + snprintf(str, MIF_MAX_STR_LEN, "%s: BAD CONFIG", + ld->mc->name); + mif_err("%s: ERR! %s INVALID config 0x%02X\n", + ld->name, get_dev_name(dev), hdr[0]); + pr_ipc(1, str, hdr, 4); + rcvd = -EBADMSG; + goto exit; } - 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); + /* Verify the total length of the frame (data + padding) */ + tot = sipc5_get_total_len(hdr); + if (unlikely(tot > rest)) { + char str[MIF_MAX_STR_LEN]; + snprintf(str, MIF_MAX_STR_LEN, "%s: BAD LENGTH", + ld->mc->name); + mif_err("%s: ERR! %s tot %d > rest %d\n", + ld->name, get_dev_name(dev), tot, rest); + pr_ipc(1, str, hdr, 4); + rcvd = -EBADMSG; +#if defined(CONFIG_MACH_C1_KOR_SKT) || defined(CONFIG_MACH_C1_KOR_KT) || defined(CONFIG_MACH_C1_KOR_LGT) + return rcvd; +#else + goto exit; +#endif } - break; - default: - mif_info("%s: unknown command 0x%04X\n", ld->name, cmd); - break; + /* 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)); + rcvd = -ENOMEM; + goto exit; + } + + /* Set the attribute of the skb as "single frame" */ + skbpriv(skb)->single_frame = true; + + /* Read the frame from the RXQ */ + dst = skb_put(skb, tot); + if (unlikely(dpld->strict_io_access)) + circ_read16_from_io(dst, src, qsize, idx, tot); + else + circ_read(dst, src, qsize, idx, tot); + +#ifdef DEBUG_MODEM_IF + /* Take a log for debugging */ + if (unlikely(dev == IPC_FMT)) { + size_t len = (skb->len > 32) ? 32 : skb->len; + char str[MIF_MAX_STR_LEN]; + snprintf(str, MIF_MAX_STR_LEN, "%s: CP2MIF", + ld->mc->name); + pr_ipc(0, str, skb->data, len); + } +#endif + +#ifdef DEBUG_MODEM_IF + /* Verify data copied to the skb */ + if (ld->aligned && memcmp16_to_io((src + idx), dst, 4)) { + mif_err("%s: memcmp16_to_io fail\n", ld->name); + rcvd = -EIO; + goto exit; + } +#endif + + /* Store the skb to the corresponding skb_rxq */ + skb_queue_tail(ld->skb_rxq[dev], skb); + + /* Calculate new idx value */ + rest -= tot; + idx += tot; + if (unlikely(idx >= qsize)) + idx -= qsize; } + +exit: +#ifdef DEBUG_MODEM_IF + if (rcvd < 0) + trigger_forced_cp_crash(dpld); +#endif + + /* Update tail (out) pointer to empty out the RXQ */ + set_rxq_tail(dpld, dev, dcst.in); + + return rcvd; } -static void udl_command_handler(struct dpram_link_device *dpld, u16 cmd) +/** + * msg_handler: receives IPC messages from every RXQ + * @dpld: pointer to an instance of dpram_link_device structure + * @stat: pointer to an instance of mem_status structure + * + * 1) Receives all IPC message frames currently in every DPRAM RXQ. + * 2) Sends RES_ACK responses if there are REQ_ACK requests from a CP. + * 3) Completes all threads waiting for the corresponding RES_ACK from a CP if + * there is any RES_ACK response. + */ +static void msg_handler(struct dpram_link_device *dpld, struct mem_status *stat) { struct link_device *ld = &dpld->ld; + int i = 0; + int ret = 0; + u16 mask = 0; + u16 intr = stat->int2ap; - if (cmd & UDL_RESULT_FAIL) { - mif_info("%s: ERR! Command failed: %04x\n", ld->name, cmd); + if (!ipc_active(dpld)) return; + + /* Read data from DPRAM */ + for (i = 0; i < ld->max_ipc_dev; i++) { + /* Invoke an RX function only when there is data in the RXQ */ + if (unlikely(stat->head[i][RX] == stat->tail[i][RX])) { + mif_debug("%s: %s_RXQ is empty\n", + ld->name, get_dev_name(i)); + } else { + if (unlikely(ld->ipc_version < SIPC_VER_50)) + ret = rx_sipc4_frames(dpld, i, stat); + else + ret = rx_sipc5_frames(dpld, i, stat); + if (ret < 0) + reset_rxq_circ(dpld, i); + } } - 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); + /* Schedule soft IRQ for RX */ + queue_delayed_work(system_nrt_wq, &dpld->rx_dwork, 0); + + /* Check and process REQ_ACK (at this time, in == out) */ + if (unlikely(intr & INT_MASK_REQ_ACK_SET)) { + for (i = 0; i < ld->max_ipc_dev; i++) { + if (intr & get_mask_req_ack(dpld, i)) { + mif_debug("%s: set %s_RES_ACK\n", + ld->name, get_dev_name(i)); + mask |= get_mask_res_ack(dpld, i); + } + } + + send_int2cp(dpld, INT_NON_CMD(mask)); + } + + /* Check and process RES_ACK */ + if (unlikely(intr & INT_MASK_RES_ACK_SET)) { + for (i = 0; i < ld->max_ipc_dev; i++) { + if (intr & get_mask_res_ack(dpld, i)) { +#ifdef DEBUG_MODEM_IF + mif_info("%s: recv %s_RES_ACK\n", + ld->name, get_dev_name(i)); + print_circ_status(ld, i, stat); +#endif + complete(&dpld->req_ack_cmpl[i]); + } + } } } -static inline void dpram_ipc_rx(struct dpram_link_device *dpld, u16 intr) +/** + * cmd_msg_handler: processes a DPRAM command or receives IPC messages + * @dpld: pointer to an instance of dpram_link_device structure + * @stat: pointer to an instance of mem_status structure + * + * Invokes cmd_handler for a DPRAM command or msg_handler for IPC messages. + */ +static inline void cmd_msg_handler(struct dpram_link_device *dpld, + struct mem_status *stat) { - if (unlikely(INT_CMD_VALID(intr))) - command_handler(dpld, intr); - else - non_command_handler(dpld, intr); + struct dpram_ext_op *ext_op = dpld->ext_op; + struct mem_status *mst = msq_get_free_slot(&dpld->stat_list); + u16 intr = stat->int2ap; + + memcpy(mst, stat, sizeof(struct mem_status)); + + if (unlikely(INT_CMD_VALID(intr))) { + if (ext_op && ext_op->cmd_handler) + ext_op->cmd_handler(dpld, intr); + else + cmd_handler(dpld, intr); + } else { + msg_handler(dpld, stat); + } } -static inline void dpram_intr_handler(struct dpram_link_device *dpld, u16 intr) +/** + * intr_handler: processes an interrupt from a CP + * @dpld: pointer to an instance of dpram_link_device structure + * @stat: pointer to an instance of mem_status structure + * + * Call flow for normal interrupt handling: + * cmd_msg_handler -> cmd_handler -> cmd_xxx_handler + * cmd_msg_handler -> msg_handler -> rx_sipc5_frames -> ... + */ +static inline void intr_handler(struct dpram_link_device *dpld, + struct mem_status *stat) { char *name = dpld->ld.name; + u16 intr = stat->int2ap; if (unlikely(intr == INT_POWERSAFE_FAIL)) { mif_info("%s: intr == INT_POWERSAFE_FAIL\n", name); @@ -1247,131 +1569,696 @@ static inline void dpram_intr_handler(struct dpram_link_device *dpld, u16 intr) mif_info("%s: ERR! invalid intr 0x%04X\n", name, intr); } else { - mif_info("%s: ERR! invalid intr 0x%04X\n", name, intr); + mif_err("%s: ERR! invalid intr 0x%04X\n", name, intr); } + return; } if (likely(INT_VALID(intr))) - dpram_ipc_rx(dpld, intr); + cmd_msg_handler(dpld, stat); else - mif_info("%s: ERR! invalid intr 0x%04X\n", name, intr); + mif_err("%s: ERR! invalid intr 0x%04X\n", name, intr); } +/** + * ap_idpram_irq_handler: interrupt handler for an internal DPRAM in an AP + * @irq: IRQ number + * @data: pointer to a data + * + * 1) Reads the interrupt value + * 2) Performs interrupt handling + */ static irqreturn_t ap_idpram_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 = recv_intr(dpld); + struct modemlink_dpram_data *dpram = dpld->dpram; + struct mem_status stat; if (unlikely(ld->mode == LINK_MODE_OFFLINE)) return IRQ_HANDLED; - dpram_intr_handler(dpld, int2ap); + get_dpram_status(dpld, RX, &stat); + + intr_handler(dpld, &stat); + + if (likely(dpram->clear_int2ap)) + dpram->clear_int2ap(); return IRQ_HANDLED; } +/** + * cp_idpram_irq_handler: interrupt handler for an internal DPRAM in a CP + * @irq: IRQ number + * @data: pointer to a data + * + * 1) Wakes up the DPRAM + * 2) Reads the interrupt value + * 3) Performs interrupt handling + * 4) Clears the interrupt port (port = memory or register) + * 5) Allows the DPRAM to sleep + */ static irqreturn_t cp_idpram_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; - - if (unlikely(ld->mode == LINK_MODE_OFFLINE)) + struct dpram_ext_op *ext_op = dpld->ext_op; + struct mem_status stat; + + if (unlikely(ld->mode == LINK_MODE_OFFLINE)) { + mif_err("%s: ERR! ld->mode == LINK_MODE_OFFLINE\n", ld->name); + get_dpram_status(dpld, RX, &stat); +#ifdef DEBUG_MODEM_IF + print_mem_status(ld, &stat); +#endif return IRQ_HANDLED; + } if (dpram_wake_up(dpld) < 0) { - log_dpram_status(dpld); - trigger_force_cp_crash(dpld); + trigger_forced_cp_crash(dpld); return IRQ_HANDLED; } - int2ap = recv_intr(dpld); + get_dpram_status(dpld, RX, &stat); - dpram_intr_handler(dpld, int2ap); + intr_handler(dpld, &stat); - clear_intr(dpld); + if (likely(ext_op && ext_op->clear_int2ap)) + ext_op->clear_int2ap(dpld); dpram_allow_sleep(dpld); return IRQ_HANDLED; } +/** + * ext_dpram_irq_handler: interrupt handler for a normal external DPRAM + * @irq: IRQ number + * @data: pointer to a data + * + * 1) Reads the interrupt value + * 2) Performs interrupt handling + */ static irqreturn_t ext_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 = recv_intr(dpld); + struct mem_status stat; if (unlikely(ld->mode == LINK_MODE_OFFLINE)) return IRQ_HANDLED; - dpram_intr_handler(dpld, int2ap); + get_dpram_status(dpld, RX, &stat); + + intr_handler(dpld, &stat); return IRQ_HANDLED; } -static void dpram_send_ipc(struct link_device *ld, int dev, - struct io_device *iod, struct sk_buff *skb) +/** + * get_txq_space + * @dpld: pointer to an instance of dpram_link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * OUT @stat: pointer to an instance of circ_status structure + * + * Stores {start address of the buffer in a TXQ, size of the buffer, in & out + * pointer values, size of free space} into the 'stat' instance. + * + * Returns the size of free space in the buffer or an error code. + */ +static int get_txq_space(struct dpram_link_device *dpld, int dev, + struct circ_status *stat) { - struct dpram_link_device *dpld = to_dpram_link_device(ld); + struct link_device *ld = &dpld->ld; + int cnt = 0; + u32 qsize; + u32 head; + u32 tail; + int space; + + while (1) { + qsize = get_txq_buff_size(dpld, dev); + head = get_txq_head(dpld, dev); + tail = get_txq_tail(dpld, dev); + space = circ_get_space(qsize, head, tail); + + 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 (circ_valid(qsize, head, tail)) + break; + + cnt++; + mif_err("%s: ERR! invalid %s_TXQ{qsize:%d in:%d out:%d " + "space:%d}, count %d\n", + ld->name, get_dev_name(dev), qsize, head, tail, + space, cnt); + if (cnt >= MAX_RETRY_CNT) { + space = -EIO; + break; + } + + udelay(100); + } + + stat->buff = get_txq_buff(dpld, dev); + stat->qsize = qsize; + stat->in = head; + stat->out = tail; + stat->size = space; + + return space; +} + +/** + * write_ipc_to_txq + * @dpld: pointer to an instance of dpram_link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * @stat: pointer to an instance of circ_status structure + * @skb: pointer to an instance of sk_buff structure + * + * Must be invoked only when there is enough space in the TXQ. + */ +static void write_ipc_to_txq(struct dpram_link_device *dpld, int dev, + struct circ_status *stat, struct sk_buff *skb) +{ + struct link_device *ld = &dpld->ld; + u8 __iomem *buff = stat->buff; + u32 qsize = stat->qsize; + u32 in = stat->in; + u8 *src = skb->data; + u32 len = skb->len; + struct mif_irq_map map; + + /* Write data to the TXQ */ + if (unlikely(dpld->strict_io_access)) + circ_write16_to_io(buff, src, qsize, in, len); + else + circ_write(buff, src, qsize, in, len); + + /* Update new head (in) pointer */ + set_txq_head(dpld, dev, circ_new_pointer(qsize, in, len)); + + /* Take a log for debugging */ + if (dev == IPC_FMT) { +#ifdef DEBUG_MODEM_IF + char tag[MIF_MAX_STR_LEN]; + snprintf(tag, MIF_MAX_STR_LEN, "%s: MIF2CP", ld->mc->name); + pr_ipc(0, tag, src, (len > 32 ? 32 : len)); +#endif + 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); + } + +#ifdef DEBUG_MODEM_IF + /* Verify data written to the TXQ */ + if (ld->aligned && memcmp16_to_io((buff + in), src, 4)) { + mif_err("%s: memcmp16_to_io fail\n", ld->name); + trigger_forced_cp_crash(dpld); + } +#endif +} + +/** + * xmit_ipc_msg + * @dpld: pointer to an instance of dpram_link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Tries to transmit IPC messages in the skb_txq of @dev as many as possible. + * + * Returns total length of IPC messages transmitted or an error code. + */ +static int xmit_ipc_msg(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 flags; + struct circ_status stat; + int space; + int copied = 0; + + /* Acquire the spin lock for a TXQ */ + spin_lock_irqsave(&dpld->tx_lock[dev], flags); + + while (1) { + /* Get the size of free space in the TXQ */ + space = get_txq_space(dpld, dev, &stat); + if (unlikely(space < 0)) { +#ifdef DEBUG_MODEM_IF + /* Trigger a enforced CP crash */ + trigger_forced_cp_crash(dpld); +#endif + /* Empty out the TXQ */ + reset_txq_circ(dpld, dev); + copied = -EIO; + break; + } + + skb = skb_dequeue(txq); + if (unlikely(!skb)) + break; + + /* Check the free space size comparing with skb->len */ + if (unlikely(space < skb->len)) { +#ifdef DEBUG_MODEM_IF + struct mem_status mst; +#endif + /* Set res_required flag for the "dev" */ + atomic_set(&dpld->res_required[dev], 1); + + /* Take the skb back to the skb_txq */ + skb_queue_head(txq, skb); + + mif_info("%s: <called by %pf> NOSPC in %s_TXQ" + "{qsize:%u in:%u out:%u}, free:%u < len:%u\n", + ld->name, CALLER, get_dev_name(dev), + stat.qsize, stat.in, stat.out, space, skb->len); +#ifdef DEBUG_MODEM_IF + get_dpram_status(dpld, TX, &mst); + print_circ_status(ld, dev, &mst); +#endif + copied = -ENOSPC; + break; + } + + /* TX only when there is enough space in the TXQ */ + write_ipc_to_txq(dpld, dev, &stat, skb); + copied += skb->len; + dev_kfree_skb_any(skb); + } + + /* Release the spin lock */ + spin_unlock_irqrestore(&dpld->tx_lock[dev], flags); + + return copied; +} + +/** + * wait_for_res_ack + * @dpld: pointer to an instance of dpram_link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * 1) Sends an REQ_ACK interrupt for @dev to CP. + * 2) Waits for the corresponding RES_ACK for @dev from CP. + * + * Returns the return value from wait_for_completion_interruptible_timeout(). + */ +static int wait_for_res_ack(struct dpram_link_device *dpld, int dev) +{ + struct link_device *ld = &dpld->ld; + struct completion *cmpl = &dpld->req_ack_cmpl[dev]; + unsigned long timeout = msecs_to_jiffies(dpld->res_ack_wait_timeout); 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); +#ifdef DEBUG_MODEM_IF + mif_info("%s: send %s_REQ_ACK\n", ld->name, get_dev_name(dev)); +#endif + + mask = get_mask_req_ack(dpld, dev); + send_int2cp(dpld, INT_NON_CMD(mask)); + + ret = wait_for_completion_interruptible_timeout(cmpl, timeout); + /* ret == 0 on timeout, ret < 0 if interrupted */ + if (ret < 0) { + mif_info("%s: %s: wait_for_completion interrupted! (ret %d)\n", + ld->name, get_dev_name(dev), ret); + goto exit; } - if (dpld->dp_type == CP_IDPRAM) { - if (dpram_wake_up(dpld) < 0) { - trigger_force_cp_crash(dpld); - return; + if (ret == 0) { + struct mem_status mst; + get_dpram_status(dpld, TX, &mst); + + mif_info("%s: wait_for_completion TIMEOUT! (no %s_RES_ACK)\n", + ld->name, get_dev_name(dev)); + + /* + ** The TXQ must be checked whether or not it is empty, because + ** an interrupt mask can be overwritten by the next interrupt. + */ + if (mst.head[dev][TX] == mst.tail[dev][TX]) { + ret = get_txq_buff_size(dpld, dev); +#ifdef DEBUG_MODEM_IF + mif_info("%s: %s_TXQ has been emptied\n", + ld->name, get_dev_name(dev)); + print_circ_status(ld, dev, &mst); +#endif } } - if (!dpram_ipc_active(dpld)) - goto exit; +exit: + return ret; +} + +/** + * process_res_ack + * @dpld: pointer to an instance of dpram_link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * 1) Tries to transmit IPC messages in the skb_txq by invoking xmit_ipc_msg() + * function. + * 2) Sends an interrupt to CP if there is no error from xmit_ipc_msg(). + * 3) Restarts DPRAM flow control if xmit_ipc_msg() returns -ENOSPC. + * + * Returns the return value from xmit_ipc_msg(). + */ +static int process_res_ack(struct dpram_link_device *dpld, int dev) +{ + int ret; + u16 mask; + + ret = xmit_ipc_msg(dpld, dev); + if (ret > 0) { + mask = get_mask_send(dpld, dev); + send_int2cp(dpld, INT_NON_CMD(mask)); + get_dpram_status(dpld, TX, msq_get_free_slot(&dpld->stat_list)); + } + + if (ret >= 0) + atomic_set(&dpld->res_required[dev], 0); + + return ret; +} + +/** + * fmt_tx_work: performs TX for FMT IPC device under DPRAM flow control + * @work: pointer to an instance of the work_struct structure + * + * 1) Starts waiting for RES_ACK of FMT IPC device. + * 2) Returns immediately if the wait is interrupted. + * 3) Restarts DPRAM flow control if there is a timeout from the wait. + * 4) Otherwise, it performs processing RES_ACK for FMT IPC device. + */ +static void fmt_tx_work(struct work_struct *work) +{ + struct link_device *ld; + struct dpram_link_device *dpld; + unsigned long delay = 0; + int ret; + + ld = container_of(work, struct link_device, fmt_tx_dwork.work); + dpld = to_dpram_link_device(ld); + + ret = wait_for_res_ack(dpld, IPC_FMT); + /* ret < 0 if interrupted */ + if (ret < 0) + return; + + /* ret == 0 on timeout */ + if (ret == 0) { + queue_delayed_work(ld->tx_wq, ld->tx_dwork[IPC_FMT], 0); + return; + } + + ret = process_res_ack(dpld, IPC_FMT); + if (ret >= 0) { + dpram_allow_sleep(dpld); + return; + } + + /* At this point, ret < 0 */ + if (ret == -ENOSPC) + queue_delayed_work(ld->tx_wq, ld->tx_dwork[IPC_FMT], delay); +} + +/** + * raw_tx_work: performs TX for RAW IPC device under DPRAM flow control. + * @work: pointer to an instance of the work_struct structure + * + * 1) Starts waiting for RES_ACK of RAW IPC device. + * 2) Returns immediately if the wait is interrupted. + * 3) Restarts DPRAM flow control if there is a timeout from the wait. + * 4) Otherwise, it performs processing RES_ACK for RAW IPC device. + */ +static void raw_tx_work(struct work_struct *work) +{ + struct link_device *ld; + struct dpram_link_device *dpld; + unsigned long delay = 0; + int ret; + + ld = container_of(work, struct link_device, raw_tx_dwork.work); + dpld = to_dpram_link_device(ld); + + ret = wait_for_res_ack(dpld, IPC_RAW); + /* ret < 0 if interrupted */ + if (ret < 0) + return; + + /* ret == 0 on timeout */ + if (ret == 0) { + queue_delayed_work(ld->tx_wq, ld->tx_dwork[IPC_RAW], 0); + return; + } + + ret = process_res_ack(dpld, IPC_RAW); + if (ret >= 0) { + dpram_allow_sleep(dpld); + mif_netif_wake(ld); + return; + } + + /* At this point, ret < 0 */ + if (ret == -ENOSPC) + queue_delayed_work(ld->tx_wq, ld->tx_dwork[IPC_RAW], delay); +} + +/** + * rfs_tx_work: performs TX for RFS IPC device under DPRAM flow control + * @work: pointer to an instance of the work_struct structure + * + * 1) Starts waiting for RES_ACK of RFS IPC device. + * 2) Returns immediately if the wait is interrupted. + * 3) Restarts DPRAM flow control if there is a timeout from the wait. + * 4) Otherwise, it performs processing RES_ACK for RFS IPC device. + */ +static void rfs_tx_work(struct work_struct *work) +{ + struct link_device *ld; + struct dpram_link_device *dpld; + unsigned long delay = 0; + int ret; + + ld = container_of(work, struct link_device, rfs_tx_dwork.work); + dpld = to_dpram_link_device(ld); + + ret = wait_for_res_ack(dpld, IPC_RFS); + /* ret < 0 if interrupted */ + if (ret < 0) + return; + + /* ret == 0 on timeout */ + if (ret == 0) { + queue_delayed_work(ld->tx_wq, ld->tx_dwork[IPC_RFS], 0); + return; + } + + ret = process_res_ack(dpld, IPC_RFS); + if (ret >= 0) { + dpram_allow_sleep(dpld); + return; + } + + /* At this point, ret < 0 */ + if (ret == -ENOSPC) + queue_delayed_work(ld->tx_wq, ld->tx_dwork[IPC_RFS], delay); +} + +/** + * dpram_send_ipc + * @dpld: pointer to an instance of dpram_link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * 1) Tries to transmit IPC messages in the skb_txq by invoking xmit_ipc_msg() + * function. + * 2) Sends an interrupt to CP if there is no error from xmit_ipc_msg(). + * 3) Starts DPRAM flow control if xmit_ipc_msg() returns -ENOSPC. + */ +static int dpram_send_ipc(struct dpram_link_device *dpld, int dev) +{ + struct link_device *ld = &dpld->ld; + int ret; + u16 mask; if (atomic_read(&dpld->res_required[dev]) > 0) { - mif_debug("%s: %s_TXQ is full\n", ld->name, get_dev_name(dev)); + mif_info("%s: %s_TXQ is full\n", ld->name, get_dev_name(dev)); + return 0; + } + + if (dpram_wake_up(dpld) < 0) { + trigger_forced_cp_crash(dpld); + return -EIO; + } + + if (!ipc_active(dpld)) { + mif_info("%s: IPC is NOT active\n", ld->name); + ret = -EIO; goto exit; } - ret = dpram_try_ipc_tx(dpld, dev); - if (ret > 0) { + ret = xmit_ipc_msg(dpld, dev); + if (likely(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); + send_int2cp(dpld, INT_NON_CMD(mask)); + get_dpram_status(dpld, TX, msq_get_free_slot(&dpld->stat_list)); + goto exit; + } + + /* If there was no TX, just exit */ + if (ret == 0) + goto exit; + + /* At this point, ret < 0 */ + if (ret == -ENOSPC) { + /* Prohibit DPRAM from sleeping until the TXQ buffer is empty */ + if (dpram_wake_up(dpld) < 0) { + trigger_forced_cp_crash(dpld); + goto exit; + } + + /*----------------------------------------------------*/ + /* dpld->res_required[dev] was set in xmit_ipc_msg(). */ + /*----------------------------------------------------*/ + + if (dev == IPC_RAW) + mif_netif_stop(ld); + + queue_delayed_work(ld->tx_wq, ld->tx_dwork[dev], 0); } exit: - if (dpld->dp_type == CP_IDPRAM) - dpram_allow_sleep(dpld); + dpram_allow_sleep(dpld); + return ret; +} + +/** + * pm_tx_work: performs TX while DPRAM PM is locked + * @work: pointer to an instance of the work_struct structure + */ +static void pm_tx_work(struct work_struct *work) +{ + struct idpram_pm_data *pm_data; + struct idpram_pm_op *pm_op; + struct dpram_link_device *dpld; + struct link_device *ld; + struct workqueue_struct *pm_wq = system_nrt_wq; + int i; + int ret; + unsigned long delay = 0; + + pm_data = container_of(work, struct idpram_pm_data, tx_dwork.work); + dpld = container_of(pm_data, struct dpram_link_device, pm_data); + ld = &dpld->ld; + pm_op = dpld->pm_op; + + if (pm_op->locked(dpld)) { + queue_delayed_work(pm_wq, &pm_data->tx_dwork, delay); + return; + } + + /* Here, PM is not locked. */ + for (i = 0; i < ld->max_ipc_dev; i++) { + ret = dpram_send_ipc(dpld, i); + if (ret < 0) { + struct io_device *iod = dpld->iod[i]; + mif_err("%s->%s: ERR! dpram_send_ipc fail (err %d)\n", + iod->name, ld->name, ret); + } + } +} + +/** + * dpram_try_send_ipc + * @dpld: pointer to an instance of dpram_link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * @iod: pointer to an instance of the io_device structure + * @skb: pointer to an skb that will be transmitted + * + * 1) Enqueues an skb to the skb_txq for @dev in the link device instance. + * 2) Tries to transmit IPC messages in the skb_txq by invoking xmit_ipc_msg() + * function. + * 3) Sends an interrupt to CP if there is no error from xmit_ipc_msg(). + * 4) Starts DPRAM flow control if xmit_ipc_msg() returns -ENOSPC. + */ +static void dpram_try_send_ipc(struct dpram_link_device *dpld, int dev, + struct io_device *iod, struct sk_buff *skb) +{ + struct link_device *ld = &dpld->ld; + struct idpram_pm_data *pm_data = &dpld->pm_data; + struct idpram_pm_op *pm_op = dpld->pm_op; + struct workqueue_struct *pm_wq = system_nrt_wq; + unsigned long delay = msecs_to_jiffies(10); + struct sk_buff_head *txq = ld->skb_txq[dev]; + int ret; + + if (unlikely(txq->qlen >= MAX_SKB_TXQ_DEPTH)) { + mif_info("%s: %s txq->qlen %d >= %d\n", ld->name, + get_dev_name(dev), txq->qlen, MAX_SKB_TXQ_DEPTH); + dev_kfree_skb_any(skb); + return; + } + + skb_queue_tail(txq, skb); + + if (pm_op && pm_op->locked) { + if (pm_op->locked(dpld)) { + queue_delayed_work(pm_wq, &pm_data->tx_dwork, delay); + return; + } + + /* Here, PM is not locked. */ + if (work_pending(&pm_data->tx_dwork.work)) + cancel_delayed_work_sync(&pm_data->tx_dwork); + } + + ret = dpram_send_ipc(dpld, dev); + if (ret < 0) { + mif_err("%s->%s: ERR! dpram_send_ipc fail (err %d)\n", + iod->name, ld->name, ret); + } } static int dpram_send_cp_binary(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->download_binary) - return dpld->ext_op->download_binary(dpld, skb); + if (dpld->ext_op && dpld->ext_op->xmit_binary) + return dpld->ext_op->xmit_binary(dpld, skb); else return -ENODEV; } +/** + * dpram_send + * @ld: pointer to an instance of the link_device structure + * @iod: pointer to an instance of the io_device structure + * @skb: pointer to an skb that will be transmitted + * + * Returns the length of data transmitted or an error code. + * + * Normal call flow for an IPC message: + * dpram_try_send_ipc -> dpram_send_ipc -> xmit_ipc_msg -> write_ipc_to_txq + * + * Call flow on PM lock in a DPRAM IPC TXQ: + * dpram_try_send_ipc ,,, queue_delayed_work + * => pm_tx_work -> dpram_send_ipc -> xmit_ipc_msg -> write_ipc_to_txq + * + * Call flow on congestion in a DPRAM IPC TXQ: + * dpram_try_send_ipc -> xmit_ipc_msg ,,, queue_delayed_work + * => xxx_tx_work -> wait_for_res_ack + * => msg_handler + * => process_res_ack -> xmit_ipc_msg (,,, queue_delayed_work ...) + */ static int dpram_send(struct link_device *ld, struct io_device *iod, - struct sk_buff *skb) + struct sk_buff *skb) { - enum dev_format dev = iod->format; + struct dpram_link_device *dpld = to_dpram_link_device(ld); + int dev = iod->format; int len = skb->len; switch (dev) { @@ -1379,7 +2266,7 @@ static int dpram_send(struct link_device *ld, struct io_device *iod, case IPC_RAW: case IPC_RFS: if (likely(ld->mode == LINK_MODE_IPC)) { - dpram_send_ipc(ld, dev, iod, skb); + dpram_try_send_ipc(dpld, dev, iod, skb); } else { mif_info("%s: ld->mode != LINK_MODE_IPC\n", ld->name); dev_kfree_skb_any(skb); @@ -1396,23 +2283,45 @@ static int dpram_send(struct link_device *ld, struct io_device *iod, } } -static int dpram_force_dump(struct link_device *ld, struct io_device *iod) +static int dpram_xmit_boot(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->xmit_boot) + return dpld->ext_op->xmit_boot(dpld, arg); + else + return -ENODEV; +} + +static int dpram_set_dload_magic(struct link_device *ld, struct io_device *iod) { struct dpram_link_device *dpld = to_dpram_link_device(ld); - trigger_force_cp_crash(dpld); + + ld->mode = LINK_MODE_DLOAD; + + mif_err("%s: magic = 0x%08X\n", ld->name, DP_MAGIC_DMDL); + iowrite32(DP_MAGIC_DMDL, dpld->dl_map.magic); + return 0; } -static void dpram_dump_memory(struct link_device *ld, char *buff) +static int dpram_dload_firmware(struct link_device *ld, struct io_device *iod, + unsigned long arg) { struct dpram_link_device *dpld = to_dpram_link_device(ld); - u8 __iomem *base = dpld->dpctl->dp_base; - u32 size = dpld->dpctl->dp_size; - if (dpld->dp_type == CP_IDPRAM) - dpram_wake_up(dpld); + if (dpld->ext_op && dpld->ext_op->firm_update) + return dpld->ext_op->firm_update(dpld, arg); + else + return -ENODEV; +} - memcpy(buff, base, size); +static int dpram_force_dump(struct link_device *ld, struct io_device *iod) +{ + struct dpram_link_device *dpld = to_dpram_link_device(ld); + trigger_forced_cp_crash(dpld); + return 0; } static int dpram_dump_start(struct link_device *ld, struct io_device *iod) @@ -1431,13 +2340,24 @@ static int dpram_dump_update(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_update) - return dpld->ext_op->dump_update(dpld, (void *)arg); + return dpld->ext_op->dump_update(dpld, arg); + else + return -ENODEV; +} + +static int dpram_dump_finish(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_finish) + return dpld->ext_op->dump_finish(dpld, arg); else return -ENODEV; } static int dpram_ioctl(struct link_device *ld, struct io_device *iod, - unsigned int cmd, unsigned long arg) + unsigned int cmd, unsigned long arg) { struct dpram_link_device *dpld = to_dpram_link_device(ld); int err = 0; @@ -1447,17 +2367,21 @@ static int dpram_ioctl(struct link_device *ld, struct io_device *iod, switch (cmd) { case IOCTL_DPRAM_INIT_STATUS: mif_debug("%s: get dpram init status\n", ld->name); - return dpld->dpram_init_status; + return dpld->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; - } + case IOCTL_MIF_DPRAM_DUMP: + if (copy_to_user((void __user *)arg, &dpld->size, sizeof(u32))) + return -EFAULT; + capture_dpram_snapshot(ld, iod); break; + + default: + if (dpld->ext_ioctl) + return dpld->ext_ioctl(dpld, iod, cmd, arg); + + mif_err("%s: ERR! invalid cmd 0x%08X\n", ld->name, cmd); + return -EINVAL; } return err; @@ -1468,9 +2392,9 @@ static void dpram_remap_std_16k_region(struct dpram_link_device *dpld) struct dpram_ipc_16k_map *dpram_map; struct dpram_ipc_device *dev; - dpram_map = (struct dpram_ipc_16k_map *)dpld->dp_base; + dpram_map = (struct dpram_ipc_16k_map *)dpld->base; - /* magic code and access enable fields */ + /* "magic code" and "access enable" fields */ dpld->ipc_map.magic = (u16 __iomem *)&dpram_map->magic; dpld->ipc_map.access = (u16 __iomem *)&dpram_map->access; @@ -1519,167 +2443,201 @@ static void dpram_remap_std_16k_region(struct dpram_link_device *dpld) dpld->ipc_map.mbx_ap2cp = (u16 __iomem *)&dpram_map->mbx_ap2cp; } -static int dpram_table_init(struct dpram_link_device *dpld) +static int dpram_init_boot_map(struct dpram_link_device *dpld) { - struct link_device *ld = &dpld->ld; - u8 __iomem *dp_base; - int i; + u8 __iomem *dp_base = dpld->base; + u32 magic_size = DP_DLOAD_MAGIC_SIZE; + u32 mbx_size = DP_MBX_SET_SIZE; - 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)); - } 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 (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; + dpld->bt_map.buff = (u8 *)(dp_base + magic_size); + dpld->bt_map.space = dpld->size - (magic_size + mbx_size); } - /* Map for download (FOTA, UDL, etc.) */ + return 0; +} + +static int dpram_init_dload_map(struct dpram_link_device *dpld) +{ + u8 __iomem *dp_base = dpld->base; + u32 magic_size = DP_DLOAD_MAGIC_SIZE; + u32 mbx_size = DP_MBX_SET_SIZE; + 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 + magic_size); + dpld->dl_map.space = dpld->size - (magic_size + mbx_size); } - /* Map for upload mode */ + return 0; +} + +static int dpram_init_uload_map(struct dpram_link_device *dpld) +{ + u8 __iomem *dp_base = dpld->base; + u32 magic_size = DP_DLOAD_MAGIC_SIZE; + u32 mbx_size = DP_MBX_SET_SIZE; + 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); + dpld->ul_map.space = dpld->size - (magic_size + mbx_size); } return 0; } +static int dpram_init_ipc_map(struct dpram_link_device *dpld) +{ + int i; + struct link_device *ld = &dpld->ld; + + if (dpld->ext_op && dpld->ext_op->init_ipc_map) { + dpld->ext_op->init_ipc_map(dpld); + } else if (dpld->dpram->ipc_map) { + memcpy(&dpld->ipc_map, dpld->dpram->ipc_map, + sizeof(struct dpram_ipc_map)); + } else { + if (dpld->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 < ld->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; + + 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->recv_intr = recv_int2ap; + dpld->send_intr = send_int2cp; 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_txq_head = get_txq_head; + dpld->get_txq_tail = get_txq_tail; + dpld->set_txq_head = set_txq_head; + dpld->set_txq_tail = set_txq_tail; + dpld->get_txq_buff = get_txq_buff; + dpld->get_txq_buff_size = get_txq_buff_size; + dpld->get_rxq_head = get_rxq_head; + dpld->get_rxq_tail = get_rxq_tail; + dpld->set_rxq_head = set_rxq_head; + dpld->set_rxq_tail = set_rxq_tail; + dpld->get_rxq_buff = get_rxq_buff; + dpld->get_rxq_buff_size = get_rxq_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; -} - -static int dpram_link_init(struct link_device *ld, struct io_device *iod) -{ - return 0; + dpld->get_dpram_status = get_dpram_status; + dpld->ipc_rx_handler = cmd_msg_handler; + dpld->reset_dpram_ipc = reset_dpram_ipc; } static void dpram_link_terminate(struct link_device *ld, struct io_device *iod) { + if (iod->format == IPC_FMT && ld->mode == LINK_MODE_IPC) { + if (!atomic_read(&iod->opened)) { + ld->mode = LINK_MODE_OFFLINE; + mif_err("%s: %s: link mode is changed: IPC->OFFLINE\n", + iod->name, ld->name); + } + } + return; } 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 *modem = NULL; + struct modemlink_dpram_data *dpram = 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"); + /* + ** Alloc an instance of dpram_link_device structure + */ + dpld = kzalloc(sizeof(struct dpram_link_device), GFP_KERNEL); + if (!dpld) { + mif_err("ERR! kzalloc dpld fail\n"); goto err; } - mif_info("modem = %s\n", mdm_data->name); - mif_info("link device = %s\n", mdm_data->link_name); + ld = &dpld->ld; - if (!mdm_data->dpram_ctl) { - mif_info("ERR! mdm_data->dpram_ctl == NULL\n"); + /* + ** Get the modem (platform) data + */ + modem = (struct modem_data *)pdev->dev.platform_data; + if (!modem) { + mif_err("ERR! modem == 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"); + mif_info("modem = %s\n", modem->name); + mif_info("link device = %s\n", modem->link_name); + + /* + ** Retrieve modem data and DPRAM control data from the modem data + */ + ld->mdm_data = modem; + ld->name = modem->link_name; + ld->ipc_version = modem->ipc_version; + + if (!modem->dpram) { + mif_err("ERR! no modem->dpram\n"); goto err; } - ld = &dpld->ld; + dpram = modem->dpram; - /* 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; + dpld->dpram = dpram; + dpld->type = dpram->type; + dpld->ap = dpram->ap; + dpld->strict_io_access = dpram->strict_io_access; - /* 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"); + if (ld->ipc_version < SIPC_VER_50) { + if (!modem->max_ipc_dev) { + mif_err("%s: ERR! no max_ipc_dev\n", ld->name); goto err; } - ld->aligned = dpctl->aligned; - dpld->max_ipc_dev = dpctl->max_ipc_dev; + ld->aligned = dpram->aligned; + ld->max_ipc_dev = modem->max_ipc_dev; } else { ld->aligned = 1; - dpld->max_ipc_dev = MAX_SIPC5_DEV; + ld->max_ipc_dev = MAX_SIPC5_DEV; } - /* Set attributes as a link device */ - ld->init_comm = dpram_link_init; + /* + ** Set attributes as a link device + */ ld->terminate_comm = dpram_link_terminate; ld->send = dpram_send; + ld->xmit_boot = dpram_xmit_boot; + ld->dload_start = dpram_set_dload_magic; + ld->firm_update = dpram_dload_firmware; ld->force_dump = dpram_force_dump; ld->dump_start = dpram_dump_start; ld->dump_update = dpram_dump_update; + ld->dump_finish = dpram_dump_finish; + /* IOCTL extension */ ld->ioctl = dpram_ioctl; INIT_LIST_HEAD(&ld->list); @@ -1691,129 +2649,217 @@ 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 up function pointers */ + skb_queue_head_init(&ld->sk_fmt_rx_q); + skb_queue_head_init(&ld->sk_raw_rx_q); + skb_queue_head_init(&ld->sk_rfs_rx_q); + ld->skb_rxq[IPC_FMT] = &ld->sk_fmt_rx_q; + ld->skb_rxq[IPC_RAW] = &ld->sk_raw_rx_q; + ld->skb_rxq[IPC_RFS] = &ld->sk_rfs_rx_q; + + init_completion(&ld->init_cmpl); + init_completion(&ld->pif_cmpl); + + /* + ** 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); + dpld->ext_op = dpram_get_ext_op(modem->modem_type); + if (dpld->ext_op) { + if (dpld->ext_op->ioctl) + dpld->ext_ioctl = dpld->ext_op->ioctl; + + if (dpld->ext_op->wakeup && dpld->ext_op->sleep) + dpld->need_wake_up = true; + } + + /* + ** Retrieve DPRAM resource + */ + if (!dpram->base) { + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + STR_DPRAM_BASE); if (!res) { - mif_info("%s: ERR! platform_get_resource fail\n", + mif_err("%s: ERR! no DPRAM resource\n", ld->name); + goto err; + } + res_size = resource_size(res); + + dpram->base = ioremap_nocache(res->start, res_size); + if (!dpram->base) { + mif_err("%s: ERR! ioremap_nocache for BASE fail\n", ld->name); goto err; } + dpram->size = res_size; + } + dpld->base = dpram->base; + dpld->size = dpram->size; + + mif_info("%s: type %d, aligned %d, base 0x%08X, size %d\n", + ld->name, dpld->type, ld->aligned, (int)dpld->base, dpld->size); + + /* + ** Retrieve DPRAM SFR resource if exists + */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + STR_DPRAM_SFR_BASE); + if (res) { res_size = resource_size(res); + dpld->sfr_base = ioremap_nocache(res->start, res_size); + if (!dpld->sfr_base) { + mif_err("%s: ERR! ioremap_nocache for SFR fail\n", + ld->name); + goto err; + } + } + + /* + ** Initialize DPRAM maps (physical map -> logical map) + */ + ret = dpram_init_boot_map(dpld); + if (ret < 0) { + mif_err("%s: ERR! dpram_init_boot_map fail (err %d)\n", + ld->name, ret); + goto err; + } - dpctl->dp_base = ioremap_nocache(res->start, res_size); - dpctl->dp_size = res_size; + ret = dpram_init_dload_map(dpld); + if (ret < 0) { + mif_err("%s: ERR! dpram_init_dload_map fail (err %d)\n", + ld->name, ret); + goto err; } - 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); + ret = dpram_init_uload_map(dpld); + if (ret < 0) { + mif_err("%s: ERR! dpram_init_uload_map fail (err %d)\n", + ld->name, ret); + goto err; + } - /* Initialize DPRAM map (physical map -> logical map) */ - ret = dpram_table_init(dpld); + ret = dpram_init_ipc_map(dpld); if (ret < 0) { - mif_info("%s: ERR! dpram_table_init fail (err %d)\n", + mif_err("%s: ERR! dpram_init_ipc_map fail (err %d)\n", ld->name, ret); goto err; } + if (dpram->res_ack_wait_timeout > 0) + dpld->res_ack_wait_timeout = dpram->res_ack_wait_timeout; + else + dpld->res_ack_wait_timeout = RES_ACK_WAIT_TIMEOUT; + /* Disable IPC */ - set_magic(dpld, 0); - set_access(dpld, 0); - dpld->dpram_init_status = DPRAM_INIT_STATE_NONE; + if (!dpram->disabled) { + set_magic(dpld, 0); + set_access(dpld, 0); + } + dpld->init_status = DPRAM_INIT_STATE_NONE; - /* Initialize locks, completions, and bottom halves */ - snprintf(dpld->wlock_name, DP_MAX_NAME_LEN, "%s_wlock", ld->name); + /* + ** Initialize locks, completions, and bottom halves + */ + snprintf(dpld->wlock_name, MIF_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); + init_completion(&dpld->udl_cmpl); + init_completion(&dpld->crash_cmpl); + + for (i = 0; i < ld->max_ipc_dev; i++) + init_completion(&dpld->req_ack_cmpl[i]); + + INIT_DELAYED_WORK(&dpld->rx_dwork, ipc_rx_work); + + for (i = 0; i < ld->max_ipc_dev; i++) { + spin_lock_init(&dpld->tx_lock[i]); + atomic_set(&dpld->res_required[i], 0); + } + + ld->tx_wq = create_singlethread_workqueue("dpram_tx_wq"); + if (!ld->tx_wq) { + mif_err("%s: ERR! fail to create tx_wq\n", ld->name); + goto err; } + INIT_DELAYED_WORK(&ld->fmt_tx_dwork, fmt_tx_work); + INIT_DELAYED_WORK(&ld->raw_tx_dwork, raw_tx_work); + INIT_DELAYED_WORK(&ld->rfs_tx_dwork, rfs_tx_work); + ld->tx_dwork[IPC_FMT] = &ld->fmt_tx_dwork; + ld->tx_dwork[IPC_RAW] = &ld->raw_tx_dwork; + ld->tx_dwork[IPC_RFS] = &ld->rfs_tx_dwork; + +#ifdef DEBUG_MODEM_IF + spin_lock_init(&dpld->stat_list.lock); + spin_lock_init(&dpld->trace_list.lock); +#endif /* Prepare a multi-purpose miscellaneous buffer */ - dpld->buff = kzalloc(dpld->dp_size, GFP_KERNEL); + dpld->buff = kzalloc(dpld->size, GFP_KERNEL); if (!dpld->buff) { - mif_info("%s: ERR! kzalloc dpld->buff fail\n", ld->name); + mif_err("%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); + /* + ** Retrieve DPRAM IRQ GPIO#, IRQ#, and IRQ flags + */ + dpld->gpio_int2ap = modem->gpio_ipc_int2ap; + dpld->gpio_cp_status = modem->gpio_cp_status; + dpld->gpio_cp_wakeup = modem->gpio_cp_wakeup; + if (dpram->type == AP_IDPRAM) { + if (!modem->gpio_ipc_int2cp) { + mif_err("%s: ERR! no gpio_ipc_int2cp\n", ld->name); goto err; } + dpld->gpio_int2cp = modem->gpio_ipc_int2cp; } - 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; + dpld->irq = modem->irq_ipc_int2ap; + + if (modem->irqf_ipc_int2ap) + dpld->irq_flags = modem->irqf_ipc_int2ap; + else + dpld->irq_flags = (IRQF_NO_SUSPEND | IRQF_TRIGGER_LOW); + + /* + ** Initialize power management (PM) for AP_IDPRAM + */ + if (dpld->type == AP_IDPRAM) { + dpld->pm_op = idpram_get_pm_op(dpld->ap); + if (!dpld->pm_op) { + mif_err("%s: no pm_op for AP_IDPRAM\n", ld->name); + goto err; + } + + ret = dpld->pm_op->pm_init(dpld, modem, pm_tx_work); + if (ret) { + mif_err("%s: pm_init fail (err %d)\n", ld->name, ret); + goto err; + } + } - /* Register DPRAM interrupt handler */ - snprintf(dpld->irq_name, DP_MAX_NAME_LEN, "%s_irq", ld->name); + /* + ** Register DPRAM interrupt handler + */ + snprintf(dpld->irq_name, MIF_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) + else if (dpld->type == CP_IDPRAM) dpld->irq_handler = cp_idpram_irq_handler; - else if (dpld->dp_type == AP_IDPRAM) + else if (dpld->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, + ret = mif_register_isr(dpld->irq, dpld->irq_handler, dpld->irq_flags, dpld->irq_name, dpld); if (ret) goto err; - else - return ld; + + return ld; err: if (dpld) { - if (dpld->buff) - kfree(dpld->buff); + kfree(dpld->buff); kfree(dpld); } diff --git a/drivers/misc/modem_if/modem_link_device_dpram.h b/drivers/misc/modem_if/modem_link_device_dpram.h index c651e51..d9ef251 100644 --- a/drivers/misc/modem_if/modem_link_device_dpram.h +++ b/drivers/misc/modem_if/modem_link_device_dpram.h @@ -1,5 +1,4 @@ /* - * Copyright (C) 2011 Google, Inc. * Copyright (C) 2010 Samsung Electronics. * * This software is licensed under the terms of the GNU General Public @@ -12,227 +11,11 @@ * 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_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 */ - -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_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 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; -}; +#include "modem_link_device_memory.h" /* magic_code + @@ -282,38 +65,110 @@ struct dpram_ipc_16k_map { u16 mbx_ap2cp; }; -#define DP_MAX_NAME_LEN 32 +enum dpram_init_status { + DPRAM_INIT_STATE_NONE, + DPRAM_INIT_STATE_READY, +}; + +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[DP_MAX_PAYLOAD_SIZE]; +}; + +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 */ + int cmd; /* AP->CP command */ +}; + +/* DPRAM upload/download header */ +struct dpram_udl_header { + u8 bop; + u16 num_frames; + u16 curr_frame; + u16 len; +#ifdef CONFIG_CDMA_MODEM_CBP82 + u8 pad; +#endif +} __packed; + +#define MAX_DUMP_SKB_SIZE 4096 + +enum idpram_link_pm_states { + IDPRAM_PM_SUSPEND_PREPARE, + IDPRAM_PM_DPRAM_POWER_DOWN, + IDPRAM_PM_SUSPEND_START, + IDPRAM_PM_RESUME_START, + IDPRAM_PM_ACTIVE, +}; + +struct idpram_pm_data { + atomic_t pm_lock; + + enum idpram_link_pm_states pm_state; + + struct completion down_cmpl; + + struct wake_lock ap_wlock; + struct wake_lock hold_wlock; + struct delayed_work tx_dwork; + struct delayed_work resume_dwork; + + struct notifier_block pm_noti; + + unsigned resume_try_cnt; + + /* the last value in the mbx_cp2ap */ + unsigned last_msg; +}; + +struct dpram_link_device; struct dpram_ext_op; +struct idpram_pm_op; struct dpram_link_device { struct link_device ld; + enum dpram_type type; /* DPRAM type */ + enum ap_type ap; /* AP type for AP_IDPRAM */ + + /* Stirct I/O access (e.g. ioread16(), etc.) is required */ + bool strict_io_access; + /* DPRAM address and size */ - u8 __iomem *dp_base; /* DPRAM base virtual address */ - u32 dp_size; /* DPRAM size */ - enum dpram_type dp_type; /* DPRAM type */ + u8 __iomem *base; /* Virtual address of DPRAM */ + u32 size; /* DPRAM size */ + + /* DPRAM SFR */ + u8 __iomem *sfr_base; /* Virtual address of SFR */ + + /* Whether or not this DPRAM can go asleep */ + bool need_wake_up; /* DPRAM IRQ GPIO# */ - unsigned gpio_dpram_int; + unsigned gpio_int2ap; + unsigned gpio_cp_status; + unsigned gpio_cp_wakeup; + unsigned gpio_int2cp; /* DPRAM IRQ from CP */ int irq; unsigned long irq_flags; - char irq_name[DP_MAX_NAME_LEN]; + char irq_name[MIF_MAX_NAME_LEN]; /* Link to DPRAM control functions dependent on each platform */ - int max_ipc_dev; - struct modemlink_dpram_control *dpctl; + struct modemlink_dpram_data *dpram; /* 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; + struct memif_boot_map bt_map; + struct memif_dload_map dl_map; + struct memif_uload_map ul_map; /* IPC device map */ struct dpram_ipc_map ipc_map; @@ -327,40 +182,40 @@ struct dpram_link_device { /* Wakelock for DPRAM device */ struct wake_lock wlock; - char wlock_name[DP_MAX_NAME_LEN]; + char wlock_name[MIF_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; + struct completion udl_cmpl; - /* For CP RAM dump */ + /* + ** For CP crash dump + */ + bool forced_cp_crash; 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 */ + struct timer_list crash_timer; + struct completion crash_cmpl; + /* If this field is wanted to be used, it must be initialized only in + * the "ld->dump_start" method. + */ + struct delayed_work crash_dwork; + /* Count of CP crash dump packets received */ + int crash_rcvd; /* For locking TX process */ spinlock_t tx_lock[MAX_IPC_DEV]; + /* For retransmission under DPRAM flow control after TXQ full state */ + unsigned long res_ack_wait_timeout; + atomic_t res_required[MAX_IPC_DEV]; + struct completion req_ack_cmpl[MAX_IPC_DEV]; + /* For efficient RX process */ - struct tasklet_struct rx_tsk; - struct dpram_rxb_queue rxbq[MAX_IPC_DEV]; + struct delayed_work rx_dwork; 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; @@ -369,45 +224,53 @@ struct dpram_link_device { u8 *buff; /* DPRAM IPC initialization status */ - int dpram_init_status; + int 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); + irq_handler_t irq_handler; - /* For DPRAM dump */ - void (*dpram_dump)(struct link_device *ld, char *buff); + /* For DPRAM logging */ + struct mem_status_queue stat_list; + struct trace_data_queue trace_list; /* 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); + u32 (*get_txq_head)(struct dpram_link_device *dpld, int id); + u32 (*get_txq_tail)(struct dpram_link_device *dpld, int id); + void (*set_txq_head)(struct dpram_link_device *dpld, int id, u32 in); + void (*set_txq_tail)(struct dpram_link_device *dpld, int id, u32 out); + u8 *(*get_txq_buff)(struct dpram_link_device *dpld, int id); + u32 (*get_txq_buff_size)(struct dpram_link_device *dpld, int id); + u32 (*get_rxq_head)(struct dpram_link_device *dpld, int id); + u32 (*get_rxq_tail)(struct dpram_link_device *dpld, int id); + void (*set_rxq_head)(struct dpram_link_device *dpld, int id, u32 in); + void (*set_rxq_tail)(struct dpram_link_device *dpld, int id, u32 out); + u8 *(*get_rxq_buff)(struct dpram_link_device *dpld, int id); + u32 (*get_rxq_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); + void (*get_dpram_status)(struct dpram_link_device *dpld, + enum circ_dir_type, struct mem_status *stat); + void (*ipc_rx_handler)(struct dpram_link_device *dpld, + struct mem_status *stat); + void (*reset_dpram_ipc)(struct dpram_link_device *dpld); /* Extended operations for various modems */ struct dpram_ext_op *ext_op; + + /* Power management (PM) for AP_IDPRAM */ + struct idpram_pm_data pm_data; + struct idpram_pm_op *pm_op; }; /* converts from struct link_device* to struct xxx_link_device* */ @@ -415,27 +278,65 @@ struct dpram_link_device { container_of(linkdev, struct dpram_link_device, ld) struct dpram_ext_op { + /* flag for checking whether or not a dpram_ext_op instance exists */ int exist; + /* methods for setting up DPRAM maps */ 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); + void (*init_ipc_map)(struct dpram_link_device *dpld); - int (*download_binary)(struct dpram_link_device *dpld, - struct sk_buff *skb); + /* methods for CP booting */ + int (*xmit_boot)(struct dpram_link_device *dpld, unsigned long arg); + int (*xmit_binary)(struct dpram_link_device *dpld, struct sk_buff *skb); + /* methods for DPRAM command handling */ + void (*cmd_handler)(struct dpram_link_device *dpld, u16 cmd); void (*cp_start_handler)(struct dpram_link_device *dpld); + /* method for CP firmware upgrade */ + int (*firm_update)(struct dpram_link_device *dpld, unsigned long arg); + + /* methods for CP crash dump */ 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 (*dump_update)(struct dpram_link_device *dpld, unsigned long arg); + int (*dump_finish)(struct dpram_link_device *dpld, unsigned long arg); + /* IOCTL extension */ int (*ioctl)(struct dpram_link_device *dpld, struct io_device *iod, - unsigned int cmd, unsigned long arg); + unsigned int cmd, unsigned long arg); - irqreturn_t (*irq_handler)(int irq, void *data); + /* methods for interrupt handling */ + irq_handler_t irq_handler; + void (*clear_int2ap)(struct dpram_link_device *dpld); + + /* methods for power management */ + int (*wakeup)(struct dpram_link_device *dpld); + void (*sleep)(struct dpram_link_device *dpld); }; struct dpram_ext_op *dpram_get_ext_op(enum modem_t modem); +struct idpram_pm_op { + /* flag for checking whether or not a idpram_pm_op instance exists */ + int exist; + int (*pm_init)(struct dpram_link_device *dpld, struct modem_data *modem, + void (*pm_tx_func)(struct work_struct *work)); + void (*power_down)(struct dpram_link_device *dpld); + void (*power_up)(struct dpram_link_device *dpld); + void (*halt_suspend)(struct dpram_link_device *dpld); + bool (*locked)(struct dpram_link_device *dpld); + bool (*int2cp_possible)(struct dpram_link_device *dpld); +}; + +struct idpram_pm_op *idpram_get_pm_op(enum ap_type id); + +#if 1 +#endif + +extern void set_sromc_access(bool access); + #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 index 1475a46..edaf6e5 100644 --- a/drivers/misc/modem_if/modem_link_device_dpram_ext_op.c +++ b/drivers/misc/modem_if/modem_link_device_dpram_ext_op.c @@ -25,103 +25,82 @@ #include <linux/if_arp.h> #include <linux/platform_device.h> #include <linux/kallsyms.h> -#include <linux/platform_data/modem.h> +#include <linux/suspend.h> +#include <plat/gpio-cfg.h> +#include <mach/gpio.h> +#include "modem.h" #include "modem_prj.h" -#include "modem_link_device_dpram.h" #include "modem_utils.h" +#include "modem_link_device_dpram.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) +#if defined(CONFIG_CDMA_MODEM_CBP72) || defined(CONFIG_CDMA_MODEM_CBP82) +static void cbp_init_boot_map(struct dpram_link_device *dpld) { - struct dpram_boot_map *bt_map = &dpld->bt_map; + struct memif_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; + bt_map->magic = (u32 *)dpld->base; + bt_map->buff = (u8 *)(dpld->base + DP_BOOT_BUFF_OFFSET); + bt_map->space = dpld->size - 4; } -static void cbp72_init_dl_map(struct dpram_link_device *dpld) +static void cbp_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); + dpld->dl_map.magic = (u32 *)dpld->base; + dpld->dl_map.buff = (u8 *)(dpld->base + DP_DLOAD_BUFF_OFFSET); } -static int _cbp72_edpram_wait_resp(struct dpram_link_device *dpld, u32 resp) +static int cbp_udl_wait_resp(struct dpram_link_device *dpld, u32 resp) { - struct link_device *ld = &dpld->ld; int ret; - int int2cp; + int int2ap; - ret = wait_for_completion_interruptible_timeout( - &dpld->udl_cmd_complete, UDL_TIMEOUT); + mif_debug("wait for 0x%04X\n", resp); + ret = wait_for_completion_timeout(&dpld->udl_cmpl, UDL_TIMEOUT); if (!ret) { - mif_info("%s: ERR! No UDL_CMD_RESP!!!\n", ld->name); - return -ENXIO; + mif_info("ERR! No UDL_CMD_RESP!!!\n"); + return -EIO; } - int2cp = dpld->recv_intr(dpld); - mif_debug("%s: int2cp = 0x%x\n", ld->name, int2cp); - if (resp == int2cp || int2cp == 0xA700) - return int2cp; - else + int2ap = dpld->recv_intr(dpld); + if (resp == int2ap || int2ap == CMD_UL_RECV_DONE_REQ) { + mif_debug("int2ap = 0x%04X\n", int2ap); + return int2ap; + } else { + mif_err("ERR! int2ap 0x%04X != resp 0x%04X\n", int2ap, resp); return -EINVAL; + } } -static int _cbp72_edpram_download_bin(struct dpram_link_device *dpld, +static int cbp_xmit_binary(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; + if (bf->len > dpld->bt_map.space) { + mif_info("ERR! Out of DPRAM boundary\n"); + err = -ERANGE; goto exit; } if (bf->len) - memcpy(buff, bf->data, bf->len); + memcpy16_to_io(buff, bf->data, bf->len); - init_completion(&dpld->udl_cmd_complete); +#ifdef CONFIG_CDMA_MODEM_CBP72 + init_completion(&dpld->udl_cmpl); +#endif - if (bf->req) + if (bf->req) { dpld->send_intr(dpld, (u16)bf->req); + mif_debug("send intr 0x%04X\n", (u16)bf->req); + } if (bf->resp) { - err = _cbp72_edpram_wait_resp(dpld, bf->resp); + err = cbp_udl_wait_resp(dpld, bf->resp); if (err < 0) { - mif_info("%s: ERR! wait_response fail (%d)\n", - ld->name, err); + mif_err("ERR! cbp_udl_wait_resp fail (err %d)\n", err); goto exit; } else if (err == bf->resp) { err = skb->len; @@ -133,24 +112,14 @@ exit: 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) +static int cbp_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; + dpld->ld.mode = LINK_MODE_ULOAD; - ret = del_timer(&dpld->dump_timer); + ret = del_timer(&dpld->crash_timer); wake_lock(&dpld->wlock); iowrite32(DP_MAGIC_UMDL, dpld->ul_map.magic); @@ -161,53 +130,65 @@ static int cbp72_dump_start(struct dpram_link_device *dpld) iowrite8((u8)0x0, dest + 3); iowrite8((u8)END_FLAG, dest + 4); - init_completion(&dpld->dump_start_complete); + init_completion(&dpld->crash_cmpl); return 0; } -static int _cbp72_edpram_upload(struct dpram_link_device *dpld, - struct dpram_dump_arg *dump, unsigned char __user *target) +static int cbp_dump_update(struct dpram_link_device *dpld, unsigned long arg) { - struct link_device *ld = &dpld->ld; - struct ul_header header; + struct dpram_dump_arg dump; + struct dpram_udl_header header; + unsigned char __user *target = (unsigned char __user *)arg; + int err = 0; + int resp = 0; u8 *dest = NULL; u8 *buff = NULL; - u16 plen = 0; - int err = 0; - int ret = 0; + u8 *header_buff = NULL; int buff_size = 0; + u16 plen = 0; - mif_debug("\n"); - - init_completion(&dpld->udl_cmd_complete); - - mif_debug("%s: req %x, resp %x", ld->name, dump->req, dump->resp); + err = copy_from_user(&dump, (void __user *)arg, sizeof(dump)); + if (err < 0) { + mif_err("ERR! ARG copy_from_user fail (err %d)\n", err); + goto exit; + } + mif_debug("req %x, resp %x", dump.req, dump.resp); - if (dump->req) - dpld->send_intr(dpld, (u16)dump->req); + if (dump.req) + dpld->send_intr(dpld, (u16)dump.req); - if (dump->resp) { - err = _cbp72_edpram_wait_resp(dpld, dump->resp); + if (dump.resp) { + resp = err = cbp_udl_wait_resp(dpld, dump.resp); if (err < 0) { - mif_info("%s: ERR! wait_response fail (%d)\n", - ld->name, err); + mif_info("ERR! wait_response fail (err %d)\n", err); goto exit; } } - if (dump->cmd) - return err; + if (dump.cmd) + goto exit; 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); + header_buff = vmalloc(sizeof(struct dpram_udl_header)); + if (!header_buff) { + err = -ENOMEM; + goto exit; + } + + memcpy16_from_io(header_buff, dest, sizeof(struct dpram_udl_header)); + + header.bop = *(u8 *)(header_buff); + header.num_frames = *(u16 *)(header_buff + 1); + header.curr_frame = *(u16 *)(header_buff + 3); + header.len = *(u16 *)(header_buff + 5); +#ifdef CONFIG_CDMA_MODEM_CBP82 + header.pad = *(u8 *)(header_buff + 7); +#endif - mif_debug("%s: total frame:%d, current frame:%d, data len:%d\n", - ld->name, header.total_frame, header.curr_frame, header.len); + mif_debug("total frames:%d, current frame:%d, data len:%d\n", + header.num_frames, header.curr_frame, header.len); plen = min_t(u16, header.len, DP_DEFAULT_DUMP_LEN); @@ -217,22 +198,26 @@ static int _cbp72_edpram_upload(struct dpram_link_device *dpld, 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); + memcpy16_from_io(buff, dest + sizeof(struct dpram_udl_header), plen); + err = copy_to_user(dump.buff, buff, plen); + if (err) { + mif_info("ERR! DUMP copy_to_user fail\n"); 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); + buff_size = plen; + err = copy_to_user(target + 4, &buff_size, sizeof(int)); + if (err) { + mif_info("ERR! SIZE copy_to_user fail\n"); err = -EIO; goto exit; } + /* Return response value */ + if (err == 0) + err = resp; + vfree(buff); return err; @@ -243,82 +228,169 @@ exit: wake_unlock(&dpld->wlock); return err; } +#endif -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; +#if defined(CONFIG_LTE_MODEM_CMC221) +/* +** For CMC221 SFR for IDPRAM +*/ +#define CMC_INT2CP_REG 0x10 /* Interrupt to CP */ +#define CMC_INT2AP_REG 0x50 +#define CMC_CLR_INT_REG 0x28 /* Clear Interrupt to AP */ +#define CMC_RESET_REG 0x3C +#define CMC_PUT_REG 0x40 /* AP->CP reg for hostbooting */ +#define CMC_GET_REG 0x50 /* CP->AP reg for hostbooting */ - 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; - } +/* +** 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 - return _cbp72_edpram_upload(dpld, &dump, (unsigned char __user *)arg); -} +#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 -static int cbp72_set_dl_magic(struct link_device *ld, struct io_device *iod) -{ - struct dpram_link_device *dpld = to_dpram_link_device(ld); +#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 - ld->mode = LINK_MODE_DLOAD; +#define CMC22x_DUMP_BUFF_SIZE 8192 - iowrite32(DP_MAGIC_DMDL, dpld->dl_map.magic); +/* CMC221 IDPRAM SFR */ +struct cmc221_idpram_sfr { + u16 __iomem *int2cp; + u16 __iomem *int2ap; + u16 __iomem *clr_int2ap; + u16 __iomem *reset; + u16 __iomem *msg2cp; + u16 __iomem *msg2ap; +}; - return 0; -} +struct cmc221_boot_img { + char *addr; + int size; + enum cp_boot_mode mode; + unsigned req; + unsigned resp; +}; -static int cbp72_ioctl(struct dpram_link_device *dpld, struct io_device *iod, - unsigned int cmd, unsigned long arg) +static struct cmc221_idpram_sfr cmc_sfr; + +static void cmc221_init_boot_map(struct dpram_link_device *dpld) { - struct link_device *ld = &dpld->ld; - int err = 0; + struct memif_boot_map *bt_map = &dpld->bt_map; - 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; + bt_map->buff = dpld->base; + bt_map->space = dpld->size; + bt_map->req = (u32 *)(dpld->base + DP_BOOT_REQ_OFFSET); + bt_map->resp = (u32 *)(dpld->base + DP_BOOT_RESP_OFFSET); +} - default: - mif_err("%s: ERR! invalid cmd 0x%08X\n", ld->name, cmd); - err = -EINVAL; - break; - } +static void cmc221_init_dl_map(struct dpram_link_device *dpld) +{ + dpld->dl_map.magic = (u32 *)dpld->base; + dpld->dl_map.buff = (u8 *)dpld->base; +} - return err; +static void cmc221_init_ul_map(struct dpram_link_device *dpld) +{ + dpld->ul_map.magic = (u32 *)dpld->base; + dpld->ul_map.buff = (u8 *)dpld->base; } -#endif -#if defined(CONFIG_LTE_MODEM_CMC221) -static void cmc221_init_boot_map(struct dpram_link_device *dpld) +static void cmc221_init_ipc_map(struct dpram_link_device *dpld) { - struct dpram_boot_map *bt_map = &dpld->bt_map; + struct dpram_ipc_16k_map *dpram_map; + struct dpram_ipc_device *dev; + u8 __iomem *sfr_base = dpld->sfr_base; + + dpram_map = (struct dpram_ipc_16k_map *)dpld->base; + + /* Magic code and access enable fields */ + dpld->ipc_map.magic = (u16 __iomem *)&dpram_map->magic; + dpld->ipc_map.access = (u16 __iomem *)&dpram_map->access; + + /* FMT */ + dev = &dpld->ipc_map.dev[IPC_FMT]; + + strcpy(dev->name, "FMT"); + dev->id = IPC_FMT; + + 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; + + 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; + + 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; + + /* SFR */ + cmc_sfr.int2cp = (u16 __iomem *)(sfr_base + CMC_INT2CP_REG); + cmc_sfr.int2ap = (u16 __iomem *)(sfr_base + CMC_INT2AP_REG); + cmc_sfr.clr_int2ap = (u16 __iomem *)(sfr_base + CMC_CLR_INT_REG); + cmc_sfr.reset = (u16 __iomem *)(sfr_base + CMC_RESET_REG); + cmc_sfr.msg2cp = (u16 __iomem *)(sfr_base + CMC_PUT_REG); + cmc_sfr.msg2ap = (u16 __iomem *)(sfr_base + CMC_GET_REG); + + /* Interrupt ports */ + dpld->ipc_map.mbx_cp2ap = cmc_sfr.int2ap; + dpld->ipc_map.mbx_ap2cp = cmc_sfr.int2cp; +} - 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 inline void cmc221_idpram_reset(struct dpram_link_device *dpld) +{ + iowrite16(1, cmc_sfr.reset); } -static void cmc221_init_dl_map(struct dpram_link_device *dpld) +static inline u16 cmc221_idpram_recv_msg(struct dpram_link_device *dpld) { - dpld->dl_map.magic = (u32 *)dpld->dp_base; - dpld->dl_map.buff = (u8 *)dpld->dp_base; + return ioread16(cmc_sfr.msg2ap); } -static void cmc221_init_ul_map(struct dpram_link_device *dpld) +static inline void cmc221_idpram_send_msg(struct dpram_link_device *dpld, + u16 msg) { - dpld->ul_map.magic = (u32 *)dpld->dp_base; - dpld->ul_map.buff = (u8 *)dpld->dp_base; + iowrite16(msg, cmc_sfr.msg2cp); } -static int _cmc221_idpram_wait_resp(struct dpram_link_device *dpld, u32 resp) +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; @@ -328,16 +400,14 @@ static int _cmc221_idpram_wait_resp(struct dpram_link_device *dpld, u32 resp) if (rcvd == resp) break; - rcvd = dpld->dpctl->recv_msg(); + rcvd = cmc221_idpram_recv_msg(dpld); if (rcvd == 0x9999) { - mif_info("%s: Invalid resp 0x%04X\n", - ld->name, rcvd); + mif_info("invalid resp 0x%04X\n", rcvd); panic("CP Crash ... BAD CRC in CP"); } if (count-- < 0) { - mif_info("%s: Invalid resp 0x%08X\n", - ld->name, rcvd); + mif_info("invalid resp 0x%08X\n", rcvd); return -EAGAIN; } @@ -345,20 +415,19 @@ static int _cmc221_idpram_wait_resp(struct dpram_link_device *dpld, u32 resp) } } else { while (1) { - rcvd = dpld->dpctl->recv_msg(); + rcvd = cmc221_idpram_recv_msg(dpld); 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); + mif_info("invalid resp CMC22x_CP_CAL_BAD\n"); break; } if (count-- < 0) { - mif_info("%s: Invalid resp 0x%04X\n", - ld->name, rcvd); + mif_info("invalid resp 0x%04X\n", rcvd); return -EAGAIN; } @@ -369,110 +438,104 @@ static int _cmc221_idpram_wait_resp(struct dpram_link_device *dpld, u32 resp) return rcvd; } -static int _cmc221_idpram_send_boot(struct dpram_link_device *dpld, void *arg) +static int cmc221_xmit_boot(struct dpram_link_device *dpld, unsigned long arg) { struct link_device *ld = &dpld->ld; u8 __iomem *bt_buff = dpld->bt_map.buff; - struct dpram_boot_img cp_img; + struct cmc221_boot_img cp_img; u8 *img_buff = NULL; int err = 0; int cnt = 0; + mif_info("+++\n"); ld->mode = LINK_MODE_BOOT; - dpld->dpctl->setup_speed(DPRAM_SPEED_LOW); + dpld->dpram->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); + if (mif_test_dpram(ld->name, bt_buff, dpld->bt_map.space) < 0) { + mif_info("ERR! mif_test_dpram fail!\n"); ld->mode = LINK_MODE_OFFLINE; return -EIO; } - memset(&cp_img, 0, sizeof(struct dpram_boot_img)); + memset(&cp_img, 0, sizeof(struct cmc221_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); + err = copy_from_user(&cp_img, (void __user *)arg, sizeof(cp_img)); + mif_info("CP image addr = 0x%08X, size = %d\n", + (int)cp_img.addr, cp_img.size); /* Alloc a buffer for the boot image */ - img_buff = kzalloc(dpld->bt_map.size, GFP_KERNEL); + img_buff = kzalloc(dpld->bt_map.space, GFP_KERNEL); if (!img_buff) { - mif_info("%s: ERR! kzalloc fail\n", ld->name); + mif_info("ERR! kzalloc fail\n"); 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); + err = copy_from_user(img_buff, (void __user *)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); + mif_info("ERR! Boot may be broken!!!\n"); goto err; } - dpld->dpctl->reset(); + cmc221_idpram_reset(dpld); 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); + if (cp_img.mode == CP_BOOT_MODE_NORMAL) { + mif_info("CP_BOOT_MODE_NORMAL\n"); + mif_info("send req 0x%08X\n", 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); + mif_info("wait resp 0x%08X\n", 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)); + mif_info("ERR! invalid resp 0x%08X\n", + ioread32(dpld->bt_map.resp)); goto err; } } } else { - mif_info("%s: HOST_BOOT_MODE_DUMP\n", ld->name); + mif_info("CP_BOOT_MODE_DUMP\n"); } kfree(img_buff); - mif_info("%s: Send BOOT done\n", ld->name); + mif_info("send BOOT done\n"); - dpld->dpctl->setup_speed(DPRAM_SPEED_HIGH); + dpld->dpram->setup_speed(DPRAM_SPEED_HIGH); + mif_info("---\n"); return 0; err: ld->mode = LINK_MODE_OFFLINE; kfree(img_buff); - mif_info("%s: ERR! Boot send fail!!!\n", ld->name); + mif_err("FAIL!!!\n"); + mif_info("---\n"); 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, +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); + if ((bf->offset + bf->len) > dpld->bt_map.space) { + mif_info("ERR! out of DPRAM boundary\n"); err = -EINVAL; goto exit; } @@ -481,17 +544,16 @@ static int _cmc221_idpram_download_bin(struct dpram_link_device *dpld, memcpy(buff, bf->data, bf->len); if (bf->req) - dpld->dpctl->send_msg((u16)bf->req); + cmc221_idpram_send_msg(dpld, (u16)bf->req); if (bf->resp) { - err = _cmc221_idpram_wait_resp(dpld, 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); + mif_info("ERR! wait_resp fail (err %d)\n", err); } if (bf->req == CMC22x_CAL_NV_DOWN_END) - mif_info("%s: CMC22x_CAL_NV_DOWN_END\n", ld->name); + mif_info("request CMC22x_CAL_NV_DOWN_END\n"); exit: if (err < 0) @@ -504,89 +566,84 @@ exit: return ret; } -static int cmc221_download_binary(struct dpram_link_device *dpld, +static int cmc221_xmit_binary(struct dpram_link_device *dpld, struct sk_buff *skb) { - if (dpld->dp_type == CP_IDPRAM) - return _cmc221_idpram_download_bin(dpld, skb); + if (dpld->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; + dpld->ld.mode = LINK_MODE_ULOAD; - ret = del_timer(&dpld->dump_timer); + del_timer(&dpld->crash_timer); wake_lock(&dpld->wlock); - dpld->dump_rcvd = 0; + dpld->crash_rcvd = 0; iowrite32(CMC22x_CP_DUMP_MAGIC, dpld->ul_map.magic); - init_completion(&dpld->dump_start_complete); + init_completion(&dpld->crash_cmpl); return 0; } -static void _cmc221_idpram_wait_dump(unsigned long arg) +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(); + msg = cmc221_idpram_recv_msg(dpld); if (msg == CMC22x_CP_DUMP_END) { - complete_all(&dpld->dump_recv_done); + complete_all(&dpld->crash_cmpl); return; } - if (((dpld->dump_rcvd & 0x1) == 0) && (msg == CMC22x_1ST_BUFF_FULL)) { - complete_all(&dpld->dump_recv_done); + if (((dpld->crash_rcvd & 0x1) == 0) && (msg == CMC22x_1ST_BUFF_FULL)) { + complete_all(&dpld->crash_cmpl); return; } - if (((dpld->dump_rcvd & 0x1) == 1) && (msg == CMC22x_2ND_BUFF_FULL)) { - complete_all(&dpld->dump_recv_done); + if (((dpld->crash_rcvd & 0x1) == 1) && (msg == CMC22x_2ND_BUFF_FULL)) { + complete_all(&dpld->crash_cmpl); return; } - mif_add_timer(&dpld->dump_timer, DUMP_WAIT_TIMEOUT, - _cmc221_idpram_wait_dump, (unsigned long)dpld); + mif_add_timer(&dpld->crash_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) +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); + if ((dpld->crash_rcvd & 0x1) == 0) + cmc221_idpram_send_msg(dpld, CMC22x_1ST_BUFF_READY); else - dpld->dpctl->send_msg(CMC22x_2ND_BUFF_READY); + cmc221_idpram_send_msg(dpld, CMC22x_2ND_BUFF_READY); - init_completion(&dpld->dump_recv_done); + init_completion(&dpld->crash_cmpl); - mif_add_timer(&dpld->dump_timer, DUMP_WAIT_TIMEOUT, - _cmc221_idpram_wait_dump, (unsigned long)dpld); + mif_add_timer(&dpld->crash_timer, DUMP_WAIT_TIMEOUT, + cmc221_idpram_wait_dump, (unsigned long)dpld); - ret = wait_for_completion_interruptible_timeout( - &dpld->dump_recv_done, DUMP_TIMEOUT); + ret = wait_for_completion_timeout(&dpld->crash_cmpl, DUMP_TIMEOUT); if (!ret) { - mif_info("%s: ERR! CP didn't send dump data!!!\n", ld->name); + mif_info("ERR! no dump from CP!!!\n"); goto err_out; } - if (dpld->dpctl->recv_msg() == CMC22x_CP_DUMP_END) { - mif_info("%s: CMC22x_CP_DUMP_END\n", ld->name); + if (cmc221_idpram_recv_msg(dpld) == CMC22x_CP_DUMP_END) { + mif_info("recv CMC22x_CP_DUMP_END\n"); return 0; } - if ((dpld->dump_rcvd & 0x1) == 0) + if ((dpld->crash_rcvd & 0x1) == 0) src = dpld->ul_map.buff; else src = dpld->ul_map.buff + CMC22x_DUMP_BUFF_SIZE; @@ -595,52 +652,64 @@ static int _cmc221_idpram_upload(struct dpram_link_device *dpld, ret = copy_to_user(dumparg->buff, dpld->buff, buff_size); if (ret < 0) { - mif_info("%s: ERR! copy_to_user fail\n", ld->name); + mif_info("ERR! copy_to_user fail\n"); goto err_out; } - dpld->dump_rcvd++; + dpld->crash_rcvd++; return buff_size; err_out: return -EIO; } -static int cmc221_dump_update(struct dpram_link_device *dpld, void *arg) +static int cmc221_dump_update(struct dpram_link_device *dpld, unsigned long 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); + mif_info("ERR! copy_from_user fail\n"); return ret; } - return _cmc221_idpram_upload(dpld, &dump); + 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) +static void cmc221_idpram_clr_int2ap(struct dpram_link_device *dpld) { - struct link_device *ld = &dpld->ld; - int err = 0; + iowrite16(0xFFFF, cmc_sfr.clr_int2ap); +} - 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; +static int cmc221_idpram_wakeup(struct dpram_link_device *dpld) +{ + int cnt = 0; - default: - mif_err("%s: ERR! invalid cmd 0x%08X\n", ld->name, cmd); - err = -EINVAL; - break; + gpio_set_value(dpld->gpio_cp_wakeup, 1); + + while (!gpio_get_value(dpld->gpio_cp_status)) { + if (cnt++ > 10) { + if (in_irq()) + mif_err("ERR! gpio_cp_status == 0 in IRQ\n"); + else + mif_err("ERR! gpio_cp_status == 0\n"); + return -EACCES; + } + + mif_info("gpio_cp_status == 0 (cnt %d)\n", cnt); + if (in_interrupt()) + udelay(1000); + else + usleep_range(1000, 2000); } - return err; + return 0; +} + +static void cmc221_idpram_sleep(struct dpram_link_device *dpld) +{ + gpio_set_value(dpld->gpio_cp_wakeup, 0); } #endif @@ -652,17 +721,44 @@ enum qc_dload_tag { QC_DLOAD_TAG_MAX }; +struct qc_dpram_boot_map { + u8 __iomem *buff; + u16 __iomem *frame_size; + u16 __iomem *tag; + u16 __iomem *count; +}; + +struct qc_dpram_udl_param { + unsigned char *addr; + unsigned int size; + unsigned int count; + unsigned int tag; +}; + +struct qc_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; +}; + +static struct qc_dpram_boot_map qc_bt_map; +static struct qc_dpram_udl_param qc_udl_param; +static struct qc_dpram_udl_check qc_udl_check; + 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; + struct qc_dpram_boot_map *qbt_map = &qc_bt_map; + struct modemlink_dpram_data *dpram = dpld->dpram; - 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); + qbt_map->buff = dpld->base; + qbt_map->frame_size = (u16 *)(dpld->base + dpram->boot_size_offset); + qbt_map->tag = (u16 *)(dpld->base + dpram->boot_tag_offset); + qbt_map->count = (u16 *)(dpld->base + dpram->boot_count_offset); tasklet_init(&dpld->dl_tsk, qc_dload_task, (unsigned long)dpld); } @@ -673,15 +769,15 @@ static int qc_prepare_download(struct dpram_link_device *dpld) int count = 0; while (1) { - if (dpld->udl_check.copy_start) { - dpld->udl_check.copy_start = 0; + if (qc_udl_check.copy_start) { + qc_udl_check.copy_start = 0; break; } - msleep_interruptible(10); + usleep_range(10000, 11000); count++; - if (count > 200) { + if (count > 300) { mif_err("ERR! count %d\n", count); return -1; } @@ -690,32 +786,33 @@ static int qc_prepare_download(struct dpram_link_device *dpld) return retval; } -static void _qc_do_download(struct dpram_link_device *dpld, - struct dpram_udl_param *param) +static void qc_do_download(struct dpram_link_device *dpld, + struct qc_dpram_udl_param *param) { - struct qc_dpram_boot_map *bt_map = &dpld->qc_bt_map; + struct qc_dpram_boot_map *qbt_map = &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); + if (param->size <= dpld->dpram->max_boot_frame_size) { + memcpy(qbt_map->buff, param->addr, param->size); + iowrite16(param->size, qbt_map->frame_size); + iowrite16(param->tag, qbt_map->tag); + iowrite16(param->count, qbt_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, +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; + struct qc_dpram_udl_param param; - retval = copy_from_user((void *)¶m, (void *)arg, sizeof(param)); + retval = copy_from_user((void *)¶m, (void __user *)arg, + sizeof(param)); if (retval < 0) { mif_err("ERR! copy_from_user fail\n"); return -1; @@ -729,24 +826,24 @@ static int _qc_download(struct dpram_link_device *dpld, void *arg, 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; + qc_udl_check.total_size = param.size; + qc_udl_check.rest_size = param.size; + qc_udl_check.send_size = 0; + qc_udl_check.copy_complete = 0; - dpld->udl_param.addr = img; - dpld->udl_param.size = dpld->dpctl->max_boot_frame_size; + qc_udl_param.addr = img; + qc_udl_param.size = dpld->dpram->max_boot_frame_size; if (tag == QC_DLOAD_TAG_NV) - dpld->udl_param.count = 1; + qc_udl_param.count = 1; else - dpld->udl_param.count = param.count; - dpld->udl_param.tag = tag; + qc_udl_param.count = param.count; + qc_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; + if (qc_udl_check.rest_size < dpld->dpram->max_boot_frame_size) + qc_udl_param.size = qc_udl_check.rest_size; /* Download image (binary or NV) */ - _qc_do_download(dpld, &dpld->udl_param); + qc_do_download(dpld, &qc_udl_param); /* Wait for completion */ @@ -756,18 +853,18 @@ static int _qc_download(struct dpram_link_device *dpld, void *arg, cnt_limit = 1000; while (1) { - if (dpld->udl_check.copy_complete) { - dpld->udl_check.copy_complete = 0; + if (qc_udl_check.copy_complete) { + qc_udl_check.copy_complete = 0; retval = 0; break; } - msleep(10); + usleep_range(10000, 11000); count++; if (count > cnt_limit) { - dpld->udl_check.total_size = 0; - dpld->udl_check.rest_size = 0; + qc_udl_check.total_size = 0; + qc_udl_check.rest_size = 0; mif_err("ERR! count %d\n", count); retval = -1; break; @@ -781,51 +878,51 @@ static int _qc_download(struct dpram_link_device *dpld, void *arg, static int qc_download_binary(struct dpram_link_device *dpld, void *arg) { - return _qc_download(dpld, arg, QC_DLOAD_TAG_BIN); + 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); + 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; + qc_udl_check.send_size += qc_udl_param.size; + qc_udl_check.rest_size -= qc_udl_param.size; - dpld->udl_param.addr += dpld->udl_param.size; + qc_udl_param.addr += qc_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; + if (qc_udl_check.send_size >= qc_udl_check.total_size) { + qc_udl_check.copy_complete = 1; + qc_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; + if (qc_udl_check.rest_size < dpld->dpram->max_boot_frame_size) + qc_udl_param.size = qc_udl_check.rest_size; - dpld->udl_param.count += 1; + qc_udl_param.count += 1; - _qc_do_download(dpld, &dpld->udl_param); + qc_do_download(dpld, &qc_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; + qc_udl_check.copy_start = 1; break; case 0xDBAB: - if (dpld->udl_check.total_size) + if (qc_udl_check.total_size) tasklet_schedule(&dpld->dl_tsk); break; case 0xABCD: - dpld->udl_check.boot_complete = 1; + qc_udl_check.boot_complete = 1; break; default: @@ -843,12 +940,12 @@ static int qc_boot_start(struct dpram_link_device *dpld) dpld->send_intr(dpld, mask); while (1) { - if (dpld->udl_check.boot_complete) { - dpld->udl_check.boot_complete = 0; + if (qc_udl_check.boot_complete) { + qc_udl_check.boot_complete = 0; break; } - msleep_interruptible(10); + usleep_range(10000, 11000); count++; if (count > 200) { @@ -870,7 +967,7 @@ static int qc_boot_post_process(struct dpram_link_device *dpld) break; } - msleep_interruptible(10); + usleep_range(10000, 11000); count++; if (count > 200) { @@ -900,27 +997,26 @@ static void qc_start_handler(struct dpram_link_device *dpld) 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); + data = dpld->get_rxq_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 Crash\n", dpld->ld.mc->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) +static int qc_data_upload(struct dpram_link_device *dpld, + struct qc_dpram_udl_param *param) { - struct qc_dpram_boot_map *bt_map = &dpld->qc_bt_map; + struct qc_dpram_boot_map *qbt_map = &qc_bt_map; int retval = 0; u16 intval = 0; int count = 0; while (1) { - if (!gpio_get_value(dpld->gpio_dpram_int)) { + if (!gpio_get_value(dpld->gpio_int2ap)) { intval = dpld->recv_intr(dpld); if (intval == 0xDBAB) { break; @@ -930,7 +1026,7 @@ static int _qc_data_upload(struct dpram_link_device *dpld, } } - msleep_interruptible(1); + usleep_range(1000, 2000); count++; if (count > 200) { @@ -939,10 +1035,10 @@ static int _qc_data_upload(struct dpram_link_device *dpld, } } - 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); + param->size = ioread16(qbt_map->frame_size); + memcpy(param->addr, qbt_map->buff, param->size); + param->tag = ioread16(qbt_map->tag); + param->count = ioread16(qbt_map->count); dpld->send_intr(dpld, 0xDB12); @@ -961,7 +1057,7 @@ static int qc_uload_step1(struct dpram_link_device *dpld) mif_info("+---------------------------------------------+\n"); while (1) { - if (!gpio_get_value(dpld->gpio_dpram_int)) { + if (!gpio_get_value(dpld->gpio_int2ap)) { intval = dpld->recv_intr(dpld); mif_info("intr 0x%04x\n", intval); if (intval == 0x1234) { @@ -972,7 +1068,7 @@ static int qc_uload_step1(struct dpram_link_device *dpld) } } - msleep_interruptible(1); + usleep_range(1000, 2000); count++; if (count > 200) { @@ -993,17 +1089,18 @@ static int qc_uload_step1(struct dpram_link_device *dpld) static int qc_uload_step2(struct dpram_link_device *dpld, void *arg) { int retval = 0; - struct dpram_udl_param param; + struct qc_dpram_udl_param param; - retval = copy_from_user((void *)¶m, (void *)arg, sizeof(param)); + retval = copy_from_user((void *)¶m, (void __user *)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); + retval = qc_data_upload(dpld, ¶m); if (retval < 0) { - mif_err("ERR! _qc_data_upload fail (err %d)\n", retval); + mif_err("ERR! qc_data_upload fail (err %d)\n", retval); return -1; } @@ -1025,40 +1122,39 @@ static int qc_uload_step2(struct dpram_link_device *dpld, void *arg) } static int qc_ioctl(struct dpram_link_device *dpld, struct io_device *iod, - unsigned int cmd, unsigned long arg) + 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); + mif_info("ERR! prepare_download fail\n"); 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); + mif_info("ERR! download_binary fail\n"); 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); + mif_info("ERR! download_nv fail\n"); break; case IOCTL_DPRAM_PHONE_BOOTSTART: err = qc_boot_start(dpld); if (err < 0) { - mif_info("%s: ERR! boot_start fail\n", ld->name); + mif_info("ERR! boot_start fail\n"); break; } err = qc_boot_post_process(dpld); if (err < 0) - mif_info("%s: ERR! boot_post_process fail\n", ld->name); + mif_info("ERR! boot_post_process fail\n"); break; @@ -1067,7 +1163,7 @@ static int qc_ioctl(struct dpram_link_device *dpld, struct io_device *iod, err = qc_uload_step1(dpld); if (err < 0) { enable_irq(dpld->irq); - mif_info("%s: ERR! upload_step1 fail\n", ld->name); + mif_info("ERR! upload_step1 fail\n"); } break; @@ -1075,12 +1171,12 @@ static int qc_ioctl(struct dpram_link_device *dpld, struct io_device *iod, err = qc_uload_step2(dpld, (void *)arg); if (err < 0) { enable_irq(dpld->irq); - mif_info("%s: ERR! upload_step2 fail\n", ld->name); + mif_info("ERR! upload_step2 fail\n"); } break; default: - mif_err("%s: ERR! invalid cmd 0x%08X\n", ld->name, cmd); + mif_err("ERR! invalid cmd 0x%08X\n", cmd); err = -EINVAL; break; } @@ -1091,44 +1187,402 @@ static int qc_ioctl(struct dpram_link_device *dpld, struct io_device *iod, 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; + struct link_device *ld = &dpld->ld; + struct mem_status stat; u16 int2ap = 0; - if (unlikely(ld->mode == LINK_MODE_OFFLINE)) + if (ld->mode == LINK_MODE_OFFLINE) { + int2ap = dpld->recv_intr(dpld); return IRQ_HANDLED; + } - int2ap = dpld->recv_intr(dpld); + dpld->get_dpram_status(dpld, RX, &stat); + int2ap = stat.int2ap; if (int2ap == INT_POWERSAFE_FAIL) { - mif_info("%s: int2ap == INT_POWERSAFE_FAIL\n", ld->name); + mif_info("int2ap == INT_POWERSAFE_FAIL\n"); goto exit; } if (int2ap == 0x1234 || int2ap == 0xDBAB || int2ap == 0xABCD) { qc_dload_cmd_handler(dpld, int2ap); goto exit; + } else if (int2ap == 0x4321 || int2ap == 0x5432) { + mif_err("ERR! CP error command (0x%04X)\n", int2ap); + goto exit; } if (likely(INT_VALID(int2ap))) - dpld->ipc_rx_handler(dpld, int2ap); + dpld->ipc_rx_handler(dpld, &stat); else - mif_info("%s: ERR! invalid intr 0x%04X\n", ld->name, int2ap); + mif_info("ERR! invalid intr 0x%04X\n", int2ap); exit: return IRQ_HANDLED; } #endif -static struct dpram_ext_op ext_op_set[] = { +#if defined(CONFIG_CDMA_MODEM_QSC6085) +#define CMD_CP_RAMDUMP_START_REQ 0x9200 +#define CMD_CP_RAMDUMP_SEND_REQ 0x9400 +#define CMD_CP_RAMDUMP_SEND_DONE_REQ 0x9600 + +#define CMD_CP_RAMDUMP_START_RESP 0x0300 +#define CMD_CP_RAMDUMP_SEND_RESP 0x0500 +#define CMD_CP_RAMDUMP_SEND_DONE_RESP 0x0700 + +#define QSC_UPLOAD_MODE (0x444D554C) +#define QSC_UPLOAD_MODE_COMPLETE (0xABCDEF90) + +#define RAMDUMP_CMD_TIMEOUT (5 * HZ) +#define QSC6085_RAM_SIZE (32 * 1024 * 1024) /* 32MB */ + +struct qsc6085_dump_command { + u32 addr; + u32 size; + u32 copyto_offset; +}; + +struct qsc6085_dump_status { + u32 dump_size; + u32 addr; + u32 rcvd; + u32 rest; +}; + +static struct qsc6085_dump_status qsc_dump_stat; + +static void qsc6085_dump_work(struct work_struct *work); + +static void qsc6085_init_dl_map(struct dpram_link_device *dpld) +{ + dpld->dl_map.magic = (u32 *)dpld->base; + dpld->dl_map.buff = (u8 *)(dpld->base + DP_DLOAD_BUFF_OFFSET); +} + +static void qsc6085_init_ul_map(struct dpram_link_device *dpld) +{ + int magic_size = DP_ULOAD_MAGIC_SIZE; + int cmd_size = sizeof(struct qsc6085_dump_command); + int mbx_size = DP_MBX_SET_SIZE; + + dpld->ul_map.magic = (u32 *)dpld->base; + dpld->ul_map.cmd = dpld->base + magic_size; + dpld->ul_map.cmd_size = cmd_size; + dpld->ul_map.buff = dpld->base + magic_size + cmd_size; + dpld->ul_map.space = dpld->size - (magic_size + cmd_size + mbx_size); +} + +static void qsc6085_req_active_handler(struct dpram_link_device *dpld) +{ + struct modem_ctl *mc = dpld->ld.mc; + mif_info("pda_active = %d\n", gpio_get_value(mc->gpio_pda_active)); + dpld->send_intr(dpld, INT_CMD(INT_CMD_RES_ACTIVE)); +} + +static void qsc6085_error_display_handler(struct dpram_link_device *dpld) +{ + struct link_device *ld = &dpld->ld; + struct io_device *iod; + + mif_err("recv 0xC9 (CRASH_EXIT)\n"); + mif_err("CP Crash: %s\n", dpld->get_rxq_buff(dpld, IPC_FMT)); + + iod = link_get_iod_with_format(ld, IPC_FMT); + if (iod) + iod->modem_state_changed(iod, STATE_CRASH_EXIT); +} + +static void qsc6085_start_handler(struct dpram_link_device *dpld) +{ + struct link_device *ld = &dpld->ld; + struct io_device *iod; + + mif_info("recv 0xC8 (CP_START)\n"); + + mif_info("send 0xC1 (INIT_START)\n"); + dpld->send_intr(dpld, INT_CMD(INT_CMD_INIT_START)); + + dpld->reset_dpram_ipc(dpld); + + iod = link_get_iod_with_format(ld, IPC_FMT); + if (!iod) { + mif_err("ERR! no iod\n"); + return; + } + iod->modem_state_changed(iod, STATE_ONLINE); + + mif_info("send 0xC2 (INIT_END)\n"); + dpld->send_intr(dpld, INT_CMD(INT_CMD_INIT_END)); +} + +static void qsc6085_command_handler(struct dpram_link_device *dpld, u16 cmd) +{ + switch (INT_CMD_MASK(cmd)) { + case INT_CMD_REQ_ACTIVE: + qsc6085_req_active_handler(dpld); + break; + + case INT_CMD_ERR_DISPLAY: +#ifdef CONFIG_LINK_DEVICE_S5P_IDPRAM + /* If modem crashes while PDA_SLEEP is in progres */ + dpld->pm_op->halt_suspend(dpld); +#endif + qsc6085_error_display_handler(dpld); + break; + + case INT_CMD_PHONE_START: + qsc6085_start_handler(dpld); + complete_all(&ld->init_cmpl); + break; + +#ifdef CONFIG_LINK_DEVICE_S5P_IDPRAM + case INT_CMD_IDPRAM_SUSPEND_ACK: + dpld->pm_op->power_down(dpld); + break; + + case INT_CMD_IDPRAM_WAKEUP_START: + dpld->pm_op->power_up(dpld); + break; +#endif + + case INT_CMD_NORMAL_POWER_OFF: + complete(&dpld->crash_cmpl); + qsc6085_error_display_handler(dpld); + break; + + default: + mif_err("unknown command 0x%04X\n", cmd); + break; + } +} + +static int qsc6085_download_firmware(struct dpram_link_device *dpld, + struct modem_firmware *fw) +{ + int ret = 0; + char __user *src = fw->binary; + int rest = fw->size; + char __iomem *dst = NULL; + unsigned long timeout; + u16 curr_frame = 0; + u16 len = 0; + struct dpram_udl_header header; + + header.bop = START_FLAG; + header.num_frames = DIV_ROUND_UP(len, DP_DEFAULT_WRITE_LEN); + mif_err("FW %d bytes = %d frames\n", fw->size, header.num_frames); + + while (rest > 0) { + curr_frame++; + len = min(rest, DP_DEFAULT_WRITE_LEN); + + header.curr_frame = curr_frame; + header.len = len; + mif_info(">>> frame# %u, len %u\n", curr_frame, len); + + dst = dpld->dl_map.buff; + memcpy(dst, &header, sizeof(header)); + + dst += sizeof(header); + ret = copy_from_user(dst, (void __user *)src, len); + if (ret < 0) { + mif_err("copy_from_user fail\n"); + return -EIO; + } + + dst += len; + src += len; + rest -= len; + + iowrite8(END_FLAG, (dst+3)); + + if (curr_frame == 1) { + dpld->send_intr(dpld, 0); + timeout = UDL_TIMEOUT; + } else { + dpld->send_intr(dpld, CMD_DL_SEND_REQ); + timeout = UDL_SEND_TIMEOUT; + } + + ret = wait_for_completion_timeout(&dpld->udl_cmpl, timeout); + if (!ret) { + mif_err("ERR! no response from CP\n"); + return -EIO; + } + } + + mif_err("send CMD_DL_DONE_REQ to CP\n"); + dpld->send_intr(dpld, CMD_DL_DONE_REQ); + + ret = wait_for_completion_timeout(&dpld->udl_cmpl, UDL_TIMEOUT); + if (!ret) { + mif_err("ERR! no response from CP\n"); + return -EIO; + } + + return 0; +} + +static int qsc6085_dload_firmware(struct dpram_link_device *dpld, + unsigned long arg) +{ + int ret; + struct modem_firmware fw; + mif_err("+++\n"); + + ret = copy_from_user(&fw, (void __user *)arg, sizeof(fw)); + if (ret < 0) { + mif_err("ERR! copy_from_user fail!\n"); + return ret; + } + + ret = qsc6085_download_firmware(dpld, &fw); + + mif_err("---\n"); + return ret; +} + +static int qsc6085_dump_start(struct dpram_link_device *dpld) +{ + int ret; + struct link_device *ld = &dpld->ld; + struct modem_ctl *mc = ld->mc; + struct qsc6085_dump_status *dump_stat = &qsc_dump_stat; + mif_err("+++\n"); + + init_completion(&dpld->crash_cmpl); + INIT_DELAYED_WORK(&dpld->crash_dwork, qsc6085_dump_work); + + iowrite32(QSC_UPLOAD_MODE, &dpld->ul_map.magic); + + /* reset modem so that it goes to upload mode */ + /* ap does not need to reset cp during CRASH_EXIT case */ + if (gpio_get_value(mc->gpio_phone_active)) + mc->ops.modem_reset(mc); + + dpld->send_intr(dpld, CMD_CP_RAMDUMP_START_REQ); + ret = wait_for_completion_timeout(&dpld->crash_cmpl, + RAMDUMP_CMD_TIMEOUT); + if (!ret) { + mif_err("ERR! no response to CP_RAMDUMP_START_REQ\n"); + dump_stat->dump_size = 0; + } else { + dump_stat->dump_size = QSC6085_RAM_SIZE; + dump_stat->addr = 0; + dump_stat->rcvd = 0; + dump_stat->rest = dump_stat->dump_size; + } + + queue_delayed_work(system_nrt_wq, &dpld->crash_dwork, 0); + + mif_err("---\n"); + return 0; +} + +static int qsc6085_dump_update(struct dpram_link_device *dpld, + unsigned long arg) +{ + int ret; + struct link_device *ld = &dpld->ld; + struct io_device *iod = link_get_iod_with_format(ld, IPC_RAMDUMP); + struct memif_uload_map *ul_map = &dpld->ul_map; + struct qsc6085_dump_status *dump_stat = &qsc_dump_stat; + struct qsc6085_dump_command dump_cmd; + + while (iod->sk_rx_q.qlen > 0) + usleep_range(1000, 1100); + + memset(&dump_cmd, 0, sizeof(dump_cmd)); + dump_cmd.addr = dump_stat->addr; + dump_cmd.size = min(dump_stat->rest, ul_map->space); + dump_cmd.copyto_offset = 0x38000010; + + memcpy_toio(ul_map->cmd, &dump_cmd, ul_map->cmd_size); + + dpld->send_intr(dpld, CMD_CP_RAMDUMP_SEND_REQ); + ret = wait_for_completion_timeout(&dpld->crash_cmpl, + RAMDUMP_CMD_TIMEOUT); + if (!ret) { + dump_stat->dump_size = 0; + mif_err("ERR! no response to CP_RAMDUMP_SEND_REQ\n"); + ret = -EIO; + goto exit; + } + + memcpy_fromio(dpld->buff, ul_map->buff, dump_cmd.size); + + ret = iod->recv(iod, ld, dpld->buff, dump_cmd.size); + if (ret < 0) + goto exit; + + dump_stat->addr += dump_cmd.size; + dump_stat->rcvd += dump_cmd.size; + dump_stat->rest -= dump_cmd.size; + mif_info("rest = %u bytes\n", dump_stat->rest); + + ret = dump_cmd.size; + +exit: + return ret; +} + +static void qsc6085_dump_work(struct work_struct *work) +{ + struct dpram_link_device *dpld; + struct link_device *ld; + struct qsc6085_dump_status *dump_stat = &qsc_dump_stat; + int ret; + + dpld = container_of(work, struct dpram_link_device, crash_dwork.work); + ld = &dpld->ld; + + ret = qsc6085_dump_update(dpld, 0); + if (ret > 0 && dump_stat->rest > 0) + queue_delayed_work(system_nrt_wq, &dpld->crash_dwork, 0); +} + +static int qsc6085_dump_finish(struct dpram_link_device *dpld, + unsigned long arg) +{ + int ret; + struct completion *cmpl = &dpld->crash_cmpl; + mif_err("+++\n"); + + init_completion(cmpl); + + dpld->send_intr(dpld, CMD_CP_RAMDUMP_SEND_DONE_REQ); + + ret = wait_for_completion_timeout(cmpl, RAMDUMP_CMD_TIMEOUT); + if (!ret) { + mif_err("ERR! no response to CP_RAMDUMP_SEND_DONE_REQ\n"); + ret = -EIO; + } + + mif_err("---\n"); + return ret; +} +#endif + +static struct dpram_ext_op ext_op_set[MAX_MODEM_TYPE] = { #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, + .init_boot_map = cbp_init_boot_map, + .init_dl_map = cbp_init_dl_map, + .xmit_binary = cbp_xmit_binary, + .dump_start = cbp_dump_start, + .dump_update = cbp_dump_update, + }, +#endif +#ifdef CONFIG_CDMA_MODEM_CBP82 + [VIA_CBP82] = { + .exist = 1, + .init_boot_map = cbp_init_boot_map, + .init_dl_map = cbp_init_dl_map, + .xmit_binary = cbp_xmit_binary, + .dump_start = cbp_dump_start, + .dump_update = cbp_dump_update, }, #endif #ifdef CONFIG_LTE_MODEM_CMC221 @@ -1137,10 +1591,14 @@ static struct dpram_ext_op ext_op_set[] = { .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, + .init_ipc_map = cmc221_init_ipc_map, + .xmit_boot = cmc221_xmit_boot, + .xmit_binary = cmc221_xmit_binary, .dump_start = cmc221_dump_start, .dump_update = cmc221_dump_update, - .ioctl = cmc221_ioctl, + .clear_int2ap = cmc221_idpram_clr_int2ap, + .wakeup = cmc221_idpram_wakeup, + .sleep = cmc221_idpram_sleep, }, #endif #if defined(CONFIG_CDMA_MODEM_MDM6600) @@ -1163,6 +1621,18 @@ static struct dpram_ext_op ext_op_set[] = { .irq_handler = qc_dpram_irq_handler, }, #endif +#if defined(CONFIG_CDMA_MODEM_QSC6085) + [QC_QSC6085] = { + .exist = 1, + .init_dl_map = qsc6085_init_dl_map, + .init_ul_map = qsc6085_init_ul_map, + .cmd_handler = qsc6085_command_handler, + .firm_update = qsc6085_dload_firmware, + .dump_start = qsc6085_dump_start, + .dump_update = qsc6085_dump_update, + .dump_finish = qsc6085_dump_finish, + }, +#endif }; struct dpram_ext_op *dpram_get_ext_op(enum modem_t modem) @@ -1173,3 +1643,422 @@ struct dpram_ext_op *dpram_get_ext_op(enum modem_t modem) return NULL; } +#ifdef CONFIG_LINK_DEVICE_S5P_IDPRAM +#define GPIO_IDPRAM_SFN S3C_GPIO_SFN(2) + +#define MAX_CHECK_RETRY_CNT 5 +#define MAX_RESUME_TRY_CNT 5 + +static bool s5p_idpram_is_pm_locked(struct dpram_link_device *dpld) +{ + struct modem_ctl *mc = dpld->ld.mc; + struct idpram_pm_data *pm_data = &dpld->pm_data; + + /* If PM is in SUSPEND */ + if (atomic_read(&pm_data->pm_lock) > 0) { + mif_info("in SUSPEND\n"); + return true; + } + + /* If AP is in or into LPA */ + if (!gpio_get_value(mc->gpio_pda_active)) { + mif_info("in LPA\n"); + return true; + } + + return false; +} + +static void s5p_idpram_set_pm_lock(struct dpram_link_device *dpld, int lock) +{ + struct idpram_pm_data *pm_data = &dpld->pm_data; + + /* 0 = unlock, 1 = lock */ + switch (lock) { + case 0: + if (atomic_read(&pm_data->pm_lock)) + atomic_set(&pm_data->pm_lock, lock); + break; + + case 1: + if (!atomic_read(&pm_data->pm_lock)) + atomic_set(&pm_data->pm_lock, lock); + break; + + default: + break; + } +} + +static void s5p_idpram_try_resume(struct work_struct *work) +{ + struct idpram_pm_data *pm_data; + struct dpram_link_device *dpld; + struct link_device *ld; + unsigned long delay; + u16 cmd; + mif_info("+++\n"); + + pm_data = container_of(work, struct idpram_pm_data, resume_dwork.work); + dpld = container_of(pm_data, struct dpram_link_device, pm_data); + ld = &dpld->ld; + + if (pm_data->last_msg == INT_CMD(INT_CMD_IDPRAM_RESUME_REQ)) { + pm_data->last_msg = 0; + + s5p_idpram_set_pm_lock(dpld, 0); + wake_unlock(&pm_data->hold_wlock); + + delay = msecs_to_jiffies(10); + schedule_delayed_work(&pm_data->tx_dwork, delay); + + mif_info("%s resumed\n", ld->name); + goto exit; + } + + if (pm_data->resume_try_cnt++ < MAX_RESUME_TRY_CNT) { + mif_info("%s not resumed yet\n", ld->name); + + cmd = INT_CMD(INT_CMD_IDPRAM_RESUME_REQ); + mif_info("send IDPRAM_RESUME_REQ (0x%X)\n", cmd); + dpld->send_intr(dpld, cmd); + + delay = msecs_to_jiffies(200); + schedule_delayed_work(&pm_data->resume_dwork, delay); + } else { + struct io_device *iod; + mif_err("ERR! %s resume T-I-M-E-O-U-T\n", ld->name); + + iod = link_get_iod_with_format(ld, IPC_FMT); + if (iod) + iod->modem_state_changed(iod, STATE_CRASH_EXIT); + + wake_unlock(&pm_data->hold_wlock); + + /* hold wakelock until uevnet sent to rild */ + wake_lock_timeout(&pm_data->hold_wlock, HZ*7); + s5p_idpram_set_pm_lock(dpld, 0); + } + +exit: + mif_info("---\n"); +} + +static irqreturn_t s5p_cp_dump_irq_handler(int irq, void *data) +{ + return IRQ_HANDLED; +} + +static irqreturn_t s5p_ap_wakeup_irq_handler(int irq, void *data) +{ + struct idpram_pm_data *pm_data = data; + wake_lock_timeout(&pm_data->ap_wlock, HZ*5); + return IRQ_HANDLED; +} + +static void s5p_idpram_power_down(struct dpram_link_device *dpld) +{ + struct idpram_pm_data *pm_data = &dpld->pm_data; + mif_info("+++\n"); + + pm_data->last_msg = INT_CMD(INT_CMD_IDPRAM_SUSPEND_ACK); + complete(&pm_data->down_cmpl); + + mif_info("---\n"); +} + +static void s5p_idpram_power_up(struct dpram_link_device *dpld) +{ + struct idpram_pm_data *pm_data = &dpld->pm_data; + mif_info("+++\n"); + + pm_data->last_msg = INT_CMD(INT_CMD_IDPRAM_RESUME_REQ); + pm_data->pm_state = IDPRAM_PM_ACTIVE; + + mif_info("---\n"); +} + +static void s5p_idpram_halt_suspend(struct dpram_link_device *dpld) +{ + struct idpram_pm_data *pm_data = &dpld->pm_data; + mif_info("+++\n"); + + complete(&pm_data->down_cmpl); + + mif_info("---\n"); +} + +static int s5p_idpram_prepare_suspend(struct dpram_link_device *dpld) +{ + struct link_device *ld = &dpld->ld; + struct idpram_pm_data *pm_data = &dpld->pm_data; + struct modem_ctl *mc = dpld->ld.mc; + struct completion *cmpl; + unsigned long timeout; + unsigned long rest; + int cnt = 0; + u16 cmd = INT_CMD(INT_CMD_IDPRAM_SUSPEND_REQ); + mif_info("+++\n"); + + pm_data->pm_state = IDPRAM_PM_SUSPEND_PREPARE; + pm_data->last_msg = 0; + s5p_idpram_set_pm_lock(dpld, 1); + + /* + * Because, if dpram was powered down, cp dpram random intr was + * ocurred. so, fixed by muxing cp dpram intr pin to GPIO output + * high,.. + */ + gpio_set_value(dpld->gpio_int2cp, 1); + s3c_gpio_cfgpin(dpld->gpio_int2cp, S3C_GPIO_OUTPUT); + + /* prevent PDA_ACTIVE status is low */ + gpio_set_value(mc->gpio_pda_active, 1); + + cmpl = &pm_data->down_cmpl; + timeout = IDPRAM_SUSPEND_REQ_TIMEOUT; + cnt = 0; + do { + init_completion(cmpl); + + mif_info("send IDPRAM_SUSPEND_REQ (0x%X)\n", cmd); + dpld->send_intr(dpld, cmd); + + rest = wait_for_completion_timeout(cmpl, timeout); + if (rest == 0) { + cnt++; + mif_err("timeout!!! (count = %d)\n", cnt); + if (cnt >= 3) { + mif_err("ERR! no response from CP\n"); + break; + } + } + } while (rest == 0); + + switch (pm_data->last_msg) { + case INT_CMD(INT_CMD_IDPRAM_SUSPEND_ACK): + mif_info("recv IDPRAM_SUSPEND_ACK (0x%X)\n", pm_data->last_msg); + pm_data->pm_state = IDPRAM_PM_DPRAM_POWER_DOWN; + break; + + default: + mif_err("ERR! %s down or not ready!!! (intr 0x%04X)\n", + ld->name, dpld->recv_intr(dpld)); + timeout = msecs_to_jiffies(500); + wake_lock_timeout(&pm_data->hold_wlock, timeout); + s5p_idpram_set_pm_lock(dpld, 0); + break; + } + + mif_info("---\n"); + return 0; +} + +static int s5p_idpram_resume_init(struct dpram_link_device *dpld) +{ + struct idpram_pm_data *pm_data = &dpld->pm_data; + mif_info("+++\n"); + + pm_data->pm_state = IDPRAM_PM_RESUME_START; + pm_data->last_msg = 0; + + dpld->reset_dpram_ipc(dpld); + + /* re-initialize internal dpram gpios */ + s3c_gpio_cfgpin(dpld->gpio_int2cp, GPIO_IDPRAM_SFN); + + mif_info("---\n"); + return 0; +} + +static int s5p_idpram_start_resume(struct dpram_link_device *dpld) +{ + struct idpram_pm_data *pm_data = &dpld->pm_data; + struct modem_ctl *mc = dpld->ld.mc; + unsigned long delay; + mif_info("+++ (pm_state = %d)\n", pm_data->pm_state); + + switch (pm_data->pm_state) { + /* schedule_work */ + case IDPRAM_PM_DPRAM_POWER_DOWN: + gpio_set_value(mc->gpio_pda_active, 0); + msleep(50); + + s5p_idpram_resume_init(dpld); + msleep(50); + + gpio_set_value(mc->gpio_pda_active, 1); + msleep(20); + + pm_data->resume_try_cnt = 0; + wake_lock(&pm_data->hold_wlock); + + delay = msecs_to_jiffies(20); + schedule_delayed_work(&pm_data->resume_dwork, delay); + break; + + case IDPRAM_PM_RESUME_START: + case IDPRAM_PM_SUSPEND_PREPARE: + default: + break; + } + + mif_info("---\n"); + return 0; +} + +static int s5p_idpram_notify_pm_event(struct notifier_block *this, + unsigned long event, void *v) +{ + struct idpram_pm_data *pm_data; + struct dpram_link_device *dpld; + int err; + mif_info("+++ (event 0x%08X)\n", (int)event); + + pm_data = container_of(this, struct idpram_pm_data, pm_noti); + dpld = container_of(pm_data, struct dpram_link_device, pm_data); + + switch (event) { + case PM_SUSPEND_PREPARE: + err = s5p_idpram_prepare_suspend(dpld); + break; + + case PM_POST_SUSPEND: + err = s5p_idpram_start_resume(dpld); + break; + + default: + break; + } + + mif_info("---\n"); + return NOTIFY_DONE; +} + +static int s5p_idpram_pm_init(struct dpram_link_device *dpld, + struct modem_data *modem, void (*pm_tx_func)(struct work_struct *work)) +{ + struct idpram_pm_data *pm_data = &dpld->pm_data; + int err; + unsigned gpio; + unsigned irq; + mif_info("+++\n"); + + atomic_set(&pm_data->pm_lock, 0); + + init_completion(&pm_data->down_cmpl); + + wake_lock_init(&pm_data->ap_wlock, WAKE_LOCK_SUSPEND, "ap_wakeup"); + wake_lock_init(&pm_data->hold_wlock, WAKE_LOCK_SUSPEND, "dpram_hold"); + + INIT_DELAYED_WORK(&pm_data->tx_dwork, pm_tx_func); + INIT_DELAYED_WORK(&pm_data->resume_dwork, s5p_idpram_try_resume); + + pm_data->resume_try_cnt = 0; + + /* register PM notifier */ + pm_data->pm_noti.notifier_call = s5p_idpram_notify_pm_event; + register_pm_notifier(&pm_data->pm_noti); + + /* + ** Register gpio_ap_wakeup interrupt handler + */ + gpio = modem->gpio_ap_wakeup; + irq = gpio_to_irq(gpio); + mif_info("gpio_ap_wakeup: GPIO# %d, IRQ# %d\n", gpio, irq); + + err = request_irq(irq, s5p_ap_wakeup_irq_handler, IRQF_TRIGGER_RISING, + "idpram_ap_wakeup", (void *)pm_data); + if (err) { + mif_err("ERR! request_irq(#%d) fail (err %d)\n", irq, err); + goto exit; + } + + err = enable_irq_wake(irq); + if (err) { + mif_err("ERR! enable_irq_wake(#%d) fail (err %d)\n", irq, err); + free_irq(irq, (void *)pm_data); + goto exit; + } + + /* + ** Register gpio_cp_dump_int interrupt handler for LPA mode + */ + gpio = modem->gpio_cp_dump_int; + irq = gpio_to_irq(gpio); + mif_info("gpio_cp_dump_int: GPIO# %d, IRQ# %d\n", gpio, irq); + + err = request_irq(irq, s5p_cp_dump_irq_handler, IRQF_TRIGGER_RISING, + "idpram_cp_dump", (void *)pm_data); + if (err) { + mif_err("ERR! request_irq(#%d) fail (err %d)\n", irq, err); + free_irq(gpio_to_irq(modem->gpio_ap_wakeup), (void *)pm_data); + goto exit; + } + + err = enable_irq_wake(irq); + if (err) { + mif_err("ERR! enable_irq_wake(#%d) fail (err %d)\n", irq, err); + free_irq(gpio_to_irq(modem->gpio_cp_dump_int), (void *)pm_data); + free_irq(gpio_to_irq(modem->gpio_ap_wakeup), (void *)pm_data); + goto exit; + } + +exit: + mif_err("---\n"); + return err; +} + +static bool s5p_idpram_int2cp_possible(struct dpram_link_device *dpld) +{ + struct modem_ctl *mc = dpld->ld.mc; + int i; + int level; + + for (i = 1; i <= MAX_CHECK_RETRY_CNT; i++) { + level = gpio_get_value(dpld->gpio_int2cp); + if (level) + break; + + /* CP has not yet received previous command. */ + mif_info("gpio_ipc_int2cp == 0 (count %d)\n", i); + + usleep_range(1000, 1100); + } + + for (i = 1; i <= MAX_CHECK_RETRY_CNT; i++) { + level = gpio_get_value(mc->gpio_pda_active); + if (level) + break; + + /* AP is in transition to LPA mode. */ + mif_info("gpio_pda_active == 0 (count %d)\n", i); + + usleep_range(1000, 1100); + } + + return true; +} +#endif + +static struct idpram_pm_op idpram_pm_op_set[MAX_AP_TYPE] = { +#ifdef CONFIG_LINK_DEVICE_S5P_IDPRAM + [S5P] = { + .pm_init = s5p_idpram_pm_init, + .power_down = s5p_idpram_power_down, + .power_up = s5p_idpram_power_up, + .halt_suspend = s5p_idpram_halt_suspend, + .locked = s5p_idpram_is_pm_locked, + .int2cp_possible = s5p_idpram_int2cp_possible, + }, +#endif +}; + +struct idpram_pm_op *idpram_get_pm_op(enum ap_type ap) +{ + if (idpram_pm_op_set[ap].exist) + return &idpram_pm_op_set[ap]; + 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 eb5dfc6..e50e4a8 100644 --- a/drivers/misc/modem_if/modem_link_device_hsic.c +++ b/drivers/misc/modem_if/modem_link_device_hsic.c @@ -31,7 +31,7 @@ #include <linux/suspend.h> #include <linux/version.h> -#include <linux/platform_data/modem.h> +#include "modem.h" #include "modem_prj.h" #include "modem_link_device_hsic.h" #include "modem_utils.h" @@ -269,7 +269,7 @@ static void usb_rx_complete(struct urb *urb) switch (pipe_data->format) { case IF_USB_FMT_EP: if (usb_ld->if_usb_is_main) { -// pr_urb("IPC-RX", urb); + pr_urb("IPC-RX", urb); iod_format = IPC_FMT; } else { iod_format = IPC_BOOT; @@ -477,13 +477,13 @@ static int _usb_tx_work(struct sk_buff *skb) if (!pipe_data) return -ENOENT; -/* + if (iod->format == IPC_FMT && usb_ld->if_usb_is_main) pr_skb("IPC-TX", skb); if (iod->format == IPC_RAW) mif_debug("TX[RAW]\n"); -*/ + return usb_tx_urb_with_skb(usb_ld->usbdev, skb, pipe_data); } @@ -741,11 +741,11 @@ static inline int link_pm_slave_wake(struct link_pm_data *pm_data) != HOSTWAKE_TRIGLEVEL) { if (gpio_get_value(pm_data->gpio_link_slavewake)) { gpio_set_value(pm_data->gpio_link_slavewake, 0); - mif_debug("gpio [SWK] set [0]\n"); + mif_info("gpio [SWK] set [0]\n"); mdelay(5); } gpio_set_value(pm_data->gpio_link_slavewake, 1); - mif_debug("gpio [SWK] set [1]\n"); + mif_info("gpio [SWK] set [1]\n"); mdelay(5); /* wait host wake signal*/ @@ -860,7 +860,7 @@ static irqreturn_t link_pm_irq_handler(int irq, void *data) runtime pm status changes to ACTIVE */ value = gpio_get_value(pm_data->gpio_link_hostwake); - mif_debug("gpio [HWK] get [%d]\n", value); + mif_info("gpio [HWK] get [%d]\n", value); /* * igonore host wakeup interrupt at suspending kernel @@ -975,9 +975,7 @@ 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); -#ifdef CONFIG_UMTS_MODEM_XMM6262 struct modem_ctl *mc = if_usb_get_modemctl(pm_data); -#endif switch (event) { case PM_SUSPEND_PREPARE: @@ -986,13 +984,11 @@ static int link_pm_notifier_event(struct notifier_block *this, case PM_RESTORE_PREPARE: #endif pm_data->dpm_suspending = true; -#ifdef CONFIG_UMTS_MODEM_XMM6262 /* set PDA Active High if previous state was LPA */ 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; case PM_POST_SUSPEND: @@ -1006,15 +1002,14 @@ static int link_pm_notifier_event(struct notifier_block *this, queue_delayed_work(pm_data->wq, &pm_data->link_pm_work, 0); mif_info("post resume\n"); - } -#ifdef CONFIG_UMTS_MODEM_XMM6262 + } else { /* 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); + 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; } @@ -1458,8 +1453,7 @@ static int usb_link_pm_init(struct usb_link_device *usb_ld, void *data) struct modem_data *pdata = (struct modem_data *)pdev->dev.platform_data; struct modemlink_pm_data *pm_pdata; - struct link_pm_data *pm_data = - kzalloc(sizeof(struct link_pm_data), GFP_KERNEL); + struct link_pm_data *pm_data; if (!pdata || !pdata->link_pm_data) { mif_err("platform data is NULL\n"); @@ -1467,6 +1461,7 @@ static int usb_link_pm_init(struct usb_link_device *usb_ld, void *data) } pm_pdata = pdata->link_pm_data; + pm_data = kzalloc(sizeof(struct link_pm_data), GFP_KERNEL); if (!pm_data) { mif_err("link_pm_data is NULL\n"); return -ENOMEM; diff --git a/drivers/misc/modem_if/modem_link_device_memory.c b/drivers/misc/modem_if/modem_link_device_memory.c new file mode 100644 index 0000000..9e49e50 --- /dev/null +++ b/drivers/misc/modem_if/modem_link_device_memory.c @@ -0,0 +1,496 @@ +/* + * Copyright (C) 2011 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/suspend.h> +#include <plat/gpio-cfg.h> +#include <mach/gpio.h> + +#include "modem.h" +#include "modem_prj.h" +#include "modem_utils.h" +#include "modem_link_device_memory.h" +#ifdef CONFIG_LINK_DEVICE_DPRAM +#include "modem_link_device_dpram.h" +#endif + +/** + * msq_get_free_slot + * @trq : pointer to an instance of mem_status_queue structure + * + * Succeeds always by dropping the oldest slot if a "msq" is full. + */ +struct mem_status *msq_get_free_slot(struct mem_status_queue *msq) +{ + int qsize = MAX_MEM_LOG_CNT; + int in; + int out; + unsigned long flags; + struct mem_status *stat; + + spin_lock_irqsave(&msq->lock, flags); + + in = msq->in; + out = msq->out; + + if (circ_get_space(qsize, in, out) < 1) { + /* Make the oldest slot empty */ + out++; + msq->out = (out == qsize) ? 0 : out; + } + + /* Get a free slot */ + stat = &msq->stat[in]; + + /* Make it as "data" slot */ + in++; + msq->in = (in == qsize) ? 0 : in; + + spin_unlock_irqrestore(&msq->lock, flags); + + memset(stat, 0, sizeof(struct mem_status)); + + return stat; +} + +struct mem_status *msq_get_data_slot(struct mem_status_queue *msq) +{ + int qsize = MAX_MEM_LOG_CNT; + int in; + int out; + unsigned long flags; + struct mem_status *stat; + + spin_lock_irqsave(&msq->lock, flags); + + in = msq->in; + out = msq->out; + + if (in == out) { + stat = NULL; + goto exit; + } + + /* Get a data slot */ + stat = &msq->stat[out]; + + /* Make it "free" slot */ + out++; + msq->out = (out == qsize) ? 0 : out; + +exit: + spin_unlock_irqrestore(&msq->lock, flags); + return stat; +} + +/** + * memcpy16_from_io + * @to: pointer to "real" memory + * @from: pointer to IO memory + * @count: data length in bytes to be copied + * + * Copies data from IO memory space to "real" memory space. + */ +void memcpy16_from_io(const void *to, const void __iomem *from, u32 count) +{ + u16 *d = (u16 *)to; + u16 *s = (u16 *)from; + u32 words = count >> 1; + while (words--) + *d++ = ioread16(s++); +} + +/** + * memcpy16_to_io + * @to: pointer to IO memory + * @from: pointer to "real" memory + * @count: data length in bytes to be copied + * + * Copies data from "real" memory space to IO memory space. + */ +void memcpy16_to_io(const void __iomem *to, const void *from, u32 count) +{ + u16 *d = (u16 *)to; + u16 *s = (u16 *)from; + u32 words = count >> 1; + while (words--) + iowrite16(*s++, d++); +} + +/** + * memcmp16_to_io + * @to: pointer to IO memory + * @from: pointer to "real" memory + * @count: data length in bytes to be compared + * + * Compares data from "real" memory space to IO memory space. + */ +int memcmp16_to_io(const void __iomem *to, const void *from, u32 count) +{ + u16 *d = (u16 *)to; + u16 *s = (u16 *)from; + int words = count >> 1; + int diff = 0; + int i; + u16 d1; + u16 s1; + + for (i = 0; i < words; 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; +} + +/** + * circ_read16_from_io + * @dst: start address of the destination buffer + * @src: start address of the buffer in a circular queue + * @qsize: size of the circular queue + * @out: offset to read + * @len: length of data to be read + * + * Should be invoked after checking data length + */ +void circ_read16_from_io(void *dst, void *src, u32 qsize, u32 out, u32 len) +{ + if ((out + len) <= qsize) { + /* ----- (out) (in) ----- */ + /* ----- 7f 00 00 7e ----- */ + memcpy16_from_io(dst, (src + out), len); + } else { + /* (in) ----------- (out) */ + /* 00 7e ----------- 7f 00 */ + unsigned len1 = qsize - out; + + /* 1) data start (out) ~ buffer end */ + memcpy16_from_io(dst, (src + out), len1); + + /* 2) buffer start ~ data end (in - 1) */ + memcpy16_from_io((dst + len1), src, (len - len1)); + } +} + +/** + * circ_write16_to_io + * @dst: pointer to the start of the circular queue + * @src: pointer to the source + * @qsize: size of the circular queue + * @in: offset to write + * @len: length of data to be written + * + * Should be invoked after checking free space + */ +void circ_write16_to_io(void *dst, void *src, u32 qsize, u32 in, u32 len) +{ + u32 space; + + if ((in + len) < qsize) { + /* (in) ----------- (out) */ + /* 00 7e ----------- 7f 00 */ + memcpy16_to_io((dst + in), src, len); + } else { + /* ----- (out) (in) ----- */ + /* ----- 7f 00 00 7e ----- */ + + /* 1) space start (in) ~ buffer end */ + space = qsize - in; + memcpy16_to_io((dst + in), src, ((len > space) ? space : len)); + + /* 2) buffer start ~ data end */ + if (len > space) + memcpy16_to_io(dst, (src + space), (len - space)); + } +} + +/** + * copy_circ_to_user + * @dst: start address of the destination buffer + * @src: start address of the buffer in a circular queue + * @qsize: size of the circular queue + * @out: offset to read + * @len: length of data to be read + * + * Should be invoked after checking data length + */ +int copy_circ_to_user(void __user *dst, void *src, u32 qsize, u32 out, u32 len) +{ + if ((out + len) <= qsize) { + /* ----- (out) (in) ----- */ + /* ----- 7f 00 00 7e ----- */ + if (copy_to_user(dst, (src + out), len)) { + mif_err("ERR! <called by %pf> copy_to_user fail\n", + CALLER); + return -EFAULT; + } + } else { + /* (in) ----------- (out) */ + /* 00 7e ----------- 7f 00 */ + unsigned len1 = qsize - out; + + /* 1) data start (out) ~ buffer end */ + if (copy_to_user(dst, (src + out), len1)) { + mif_err("ERR! <called by %pf> copy_to_user fail\n", + CALLER); + return -EFAULT; + } + + /* 2) buffer start ~ data end (in?) */ + if (copy_to_user((dst + len1), src, (len - len1))) { + mif_err("ERR! <called by %pf> copy_to_user fail\n", + CALLER); + return -EFAULT; + } + } + + return 0; +} + +/** + * copy_user_to_circ + * @dst: pointer to the start of the circular queue + * @src: pointer to the source + * @qsize: size of the circular queue + * @in: offset to write + * @len: length of data to be written + * + * Should be invoked after checking free space + */ +int copy_user_to_circ(void *dst, void __user *src, u32 qsize, u32 in, u32 len) +{ + u32 space; + u32 len1; + + if ((in + len) < qsize) { + /* (in) ----------- (out) */ + /* 00 7e ----------- 7f 00 */ + if (copy_from_user((dst + in), src, len)) { + mif_err("ERR! <called by %pf> copy_from_user fail\n", + CALLER); + return -EFAULT; + } + } else { + /* ----- (out) (in) ----- */ + /* ----- 7f 00 00 7e ----- */ + + /* 1) space start (in) ~ buffer end */ + space = qsize - in; + len1 = (len > space) ? space : len; + if (copy_from_user((dst + in), src, len1)) { + mif_err("ERR! <called by %pf> copy_from_user fail\n", + CALLER); + return -EFAULT; + } + + /* 2) buffer start ~ data end */ + if (len > len1) { + if (copy_from_user(dst, (src + space), (len - len1))) { + mif_err("ERR! <called by %pf> copy_from_user " + "fail\n", CALLER); + return -EFAULT; + } + } + } + + return 0; +} + +/** + * print_mem_status + * @ld: pointer to an instance of link_device structure + * @mst: pointer to an instance of mem_status structure + * + * Prints a snapshot of the status of a SHM. + */ +void print_mem_status(struct link_device *ld, struct mem_status *mst) +{ + struct utc_time utc; + int us = ns2us(mst->ts.tv_nsec); + + ts2utc(&mst->ts, &utc); + pr_info("%s: %s: [%02d:%02d:%02d.%06d] " + "[%s] ACC{%X %d} " + "FMT{TI:%u TO:%u RI:%u RO:%u} " + "RAW{TI:%u TO:%u RI:%u RO:%u} " + "INTR{RX:0x%X TX:0x%X}\n", + MIF_TAG, ld->name, utc.hour, utc.min, utc.sec, us, + get_dir_str(mst->dir), mst->magic, mst->access, + mst->head[IPC_FMT][TX], mst->tail[IPC_FMT][TX], + mst->head[IPC_FMT][RX], mst->tail[IPC_FMT][RX], + mst->head[IPC_RAW][TX], mst->tail[IPC_RAW][TX], + mst->head[IPC_RAW][RX], mst->tail[IPC_RAW][RX], + mst->int2ap, mst->int2cp); +} + +/** + * print_circ_status + * @ld: pointer to an instance of link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * @mst: pointer to an instance of mem_status structure + * + * Prints a snapshot of the status of a memory + */ +void print_circ_status(struct link_device *ld, int dev, struct mem_status *mst) +{ + struct utc_time utc; + int us = ns2us(mst->ts.tv_nsec); + + if (dev > IPC_RAW) + return; + + ts2utc(&mst->ts, &utc); + pr_info("%s: %s: [%02d:%02d:%02d.%06d] " + "[%s] %s | TXQ{in:%u out:%u} RXQ{in:%u out:%u}\n", + MIF_TAG, ld->name, utc.hour, utc.min, utc.sec, us, + get_dir_str(mst->dir), get_dev_name(dev), + mst->head[dev][TX], mst->tail[dev][TX], + mst->head[dev][RX], mst->tail[dev][RX]); +} + +/** + * print_ipc_trace + * @ld: pointer to an instance of link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * @stat: pointer to an instance of circ_status structure + * @ts: pointer to an instance of timespec structure + * @buff: start address of a buffer into which RX IPC messages were copied + * @rcvd: size of data in the buffer + * + * Prints IPC messages in a local memory buffer to a kernel log. + */ +void print_ipc_trace(struct link_device *ld, int dev, struct circ_status *stat, + struct timespec *ts, u8 *buff, u32 rcvd) +{ + struct utc_time utc; + + ts2utc(ts, &utc); + + pr_info("%s: [%d-%02d-%02d %02d:%02d:%02d.%03d] " + "%s %s_RXQ {IN:%u OUT:%u LEN:%d}\n", + MIF_TAG, utc.year, utc.mon, utc.day, utc.hour, utc.min, utc.sec, + utc.msec, ld->name, get_dev_name(dev), stat->in, stat->out, + stat->size); + + mif_print_dump(buff, rcvd, 4); +} + +/** + * capture_mem_dump + * @ld: pointer to an instance of link_device structure + * @base: base virtual address to a memory interface medium + * @size: size of the memory interface medium + * + * Captures a dump for a memory interface medium. + * + * Returns the pointer to a memory dump buffer. + */ +u8 *capture_mem_dump(struct link_device *ld, u8 *base, u32 size) +{ + u8 *buff = kzalloc(size, GFP_ATOMIC); + if (!buff) { + mif_err("%s: ERR! kzalloc(%d) fail\n", ld->name, size); + return NULL; + } else { + memcpy16_from_io(buff, base, size); + return buff; + } +} + +/** + * trq_get_free_slot + * @trq : pointer to an instance of trace_data_queue structure + * + * Succeeds always by dropping the oldest slot if a "trq" is full. + */ +struct trace_data *trq_get_free_slot(struct trace_data_queue *trq) +{ + int qsize = MAX_TRACE_SIZE; + int in; + int out; + unsigned long flags; + struct trace_data *trd; + + spin_lock_irqsave(&trq->lock, flags); + + in = trq->in; + out = trq->out; + + /* The oldest slot can be dropped. */ + if (circ_get_space(qsize, in, out) < 1) { + /* Free the data buffer in the oldest slot */ + trd = &trq->trd[out]; + kfree(trd->data); + + /* Make the oldest slot empty */ + out++; + trq->out = (out == qsize) ? 0 : out; + } + + /* Get a free slot and make it occupied */ + trd = &trq->trd[in++]; + trq->in = (in == qsize) ? 0 : in; + + spin_unlock_irqrestore(&trq->lock, flags); + + memset(trd, 0, sizeof(struct trace_data)); + + return trd; +} + +struct trace_data *trq_get_data_slot(struct trace_data_queue *trq) +{ + int qsize = MAX_TRACE_SIZE; + int in; + int out; + unsigned long flags; + struct trace_data *trd; + + spin_lock_irqsave(&trq->lock, flags); + + in = trq->in; + out = trq->out; + + if (circ_get_usage(qsize, in, out) < 1) { + spin_unlock_irqrestore(&trq->lock, flags); + return NULL; + } + + /* Get a data slot and make it empty */ + trd = &trq->trd[out++]; + trq->out = (out == qsize) ? 0 : out; + + spin_unlock_irqrestore(&trq->lock, flags); + + return trd; +} + diff --git a/drivers/misc/modem_if/modem_link_device_memory.h b/drivers/misc/modem_if/modem_link_device_memory.h new file mode 100644 index 0000000..29a84ac --- /dev/null +++ b/drivers/misc/modem_if/modem_link_device_memory.h @@ -0,0 +1,409 @@ +/* + * 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_MEMORY_H__ +#define __MODEM_LINK_DEVICE_MEMORY_H__ + +#include <linux/spinlock.h> +#include <linux/wakelock.h> +#include <linux/workqueue.h> +#include <linux/timer.h> +#include <linux/notifier.h> +#if defined(CONFIG_HAS_EARLYSUSPEND) +#include <linux/earlysuspend.h> +#elif defined(CONFIG_FB) +#include <linux/fb.h> +#endif + +#include "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_DL_READY 0xA100 +#define CMD_DL_START_REQ 0x9200 +#define CMD_DL_START_RESP 0xA301 +#define CMD_DL_SEND_REQ 0x9400 +#define CMD_DL_SEND_RESP 0xA501 +#define CMD_DL_DONE_REQ 0x9600 +#define CMD_DL_DONE_RESP 0xA701 + +#define CMD_UL_RECV_RESP 0x9601 +#define CMD_UL_RECV_DONE_REQ 0xA700 +#define CMD_UL_RECV_DONE_RESP 0x9801 + +/* special interrupt cmd indicating modem boot failure. */ +#define INT_POWERSAFE_FAIL 0xDEAD + +#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_REQ_ACK_SET \ + (INT_MASK_REQ_ACK_F | INT_MASK_REQ_ACK_R | INT_MASK_REQ_ACK_RFS) + +#define INT_MASK_RES_ACK_SET \ + (INT_MASK_RES_ACK_F | INT_MASK_RES_ACK_R | INT_MASK_RES_ACK_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_POWER_OFF 0xF + +/* AP_IDPRAM PM control command with QSC6085 */ +#define INT_CMD_IDPRAM_SUSPEND_REQ 0xD +#define INT_CMD_IDPRAM_SUSPEND_ACK 0xB +#define INT_CMD_IDPRAM_WAKEUP_START 0xE +#define INT_CMD_IDPRAM_RESUME_REQ 0xC + +#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 IDPRAM_SUSPEND_REQ_TIMEOUT (50 * HZ) + +#define RES_ACK_WAIT_TIMEOUT 10 /* 10 ms */ +#define REQ_ACK_DELAY 10 /* 10 ms */ + +#ifdef DEBUG_MODEM_IF +#define MAX_RETRY_CNT 1 +#else +#define MAX_RETRY_CNT 3 +#endif + +#define MAX_SKB_TXQ_DEPTH 1024 + +struct memif_boot_map { + u32 __iomem *magic; + u8 __iomem *buff; + u32 __iomem *req; + u32 __iomem *resp; + u32 space; +}; + +struct memif_dload_map { + u32 __iomem *magic; + u8 __iomem *buff; + u32 space; +}; + +struct memif_uload_map { + u32 __iomem *magic; + u8 __iomem *cmd; + u32 cmd_size; + u8 __iomem *buff; + u32 space; +}; + +#define DP_BOOT_BUFF_OFFSET 4 +#define DP_DLOAD_MAGIC_SIZE 4 +#define DP_DLOAD_BUFF_OFFSET 4 +#define DP_ULOAD_MAGIC_SIZE 4 +#define DP_ULOAD_BUFF_OFFSET 4 +#define DP_BOOT_REQ_OFFSET 0 +#define DP_BOOT_RESP_OFFSET 8 +#define DP_MBX_SET_SIZE 4 +#define DP_MAX_PAYLOAD_SIZE 0x2000 + +enum circ_dir_type { + TX, + RX, + MAX_DIR, +}; + +enum circ_ptr_type { + HEAD, + TAIL, +}; + +static inline bool circ_valid(u32 qsize, u32 in, u32 out) +{ + if (in >= qsize) + return false; + + if (out >= qsize) + return false; + + return true; +} + +static inline u32 circ_get_space(u32 qsize, u32 in, u32 out) +{ + return (in < out) ? (out - in - 1) : (qsize + out - in - 1); +} + +static inline u32 circ_get_usage(u32 qsize, u32 in, u32 out) +{ + return (in >= out) ? (in - out) : (qsize - out + in); +} + +static inline u32 circ_new_pointer(u32 qsize, u32 p, u32 len) +{ + p += len; + return (p < qsize) ? p : (p - qsize); +} + +/** + * circ_read + * @dst: start address of the destination buffer + * @src: start address of the buffer in a circular queue + * @qsize: size of the circular queue + * @out: offset to read + * @len: length of data to be read + * + * Should be invoked after checking data length + */ +static inline void circ_read(void *dst, void *src, u32 qsize, u32 out, u32 len) +{ + unsigned len1; + + if ((out + len) <= qsize) { + /* ----- (out) (in) ----- */ + /* ----- 7f 00 00 7e ----- */ + memcpy(dst, (src + out), len); + } else { + /* (in) ----------- (out) */ + /* 00 7e ----------- 7f 00 */ + + /* 1) data start (out) ~ buffer end */ + len1 = qsize - out; + memcpy(dst, (src + out), len1); + + /* 2) buffer start ~ data end (in?) */ + memcpy((dst + len1), src, (len - len1)); + } +} + +/** + * circ_write + * @dst: pointer to the start of the circular queue + * @src: pointer to the source + * @qsize: size of the circular queue + * @in: offset to write + * @len: length of data to be written + * + * Should be invoked after checking free space + */ +static inline void circ_write(void *dst, void *src, u32 qsize, u32 in, u32 len) +{ + u32 space; + + if ((in + len) < qsize) { + /* (in) ----------- (out) */ + /* 00 7e ----------- 7f 00 */ + memcpy((dst + in), src, len); + } else { + /* ----- (out) (in) ----- */ + /* ----- 7f 00 00 7e ----- */ + + /* 1) space start (in) ~ buffer end */ + space = qsize - in; + memcpy((dst + in), src, ((len > space) ? space : len)); + + /* 2) buffer start ~ data end */ + if (len > space) + memcpy(dst, (src + space), (len - space)); + } +} + +/** + * circ_dir + * @dir: communication direction (enum circ_dir_type) + * + * Returns the direction of a circular queue + * + */ +static const inline char *circ_dir(enum circ_dir_type dir) +{ + if (dir == TX) + return "TXQ"; + else + return "RXQ"; +} + +/** + * circ_ptr + * @ptr: circular queue pointer (enum circ_ptr_type) + * + * Returns the name of a circular queue pointer + * + */ +static const inline char *circ_ptr(enum circ_ptr_type ptr) +{ + if (ptr == HEAD) + return "head"; + else + return "tail"; +} + +/** + * get_dir_str + * @dir: communication direction (enum circ_dir_type) + * + * Returns the direction of a circular queue + * + */ +static const inline char *get_dir_str(enum circ_dir_type dir) +{ + if (dir == TX) + return "AP->CP"; + else + return "CP->AP"; +} + +void memcpy16_from_io(const void *to, const void __iomem *from, u32 count); +void memcpy16_to_io(const void __iomem *to, const void *from, u32 count); +int memcmp16_to_io(const void __iomem *to, const void *from, u32 count); +void circ_read16_from_io(void *dst, void *src, u32 qsize, u32 out, u32 len); +void circ_write16_to_io(void *dst, void *src, u32 qsize, u32 in, u32 len); +int copy_circ_to_user(void __user *dst, void *src, u32 qsize, u32 out, u32 len); +int copy_user_to_circ(void *dst, void __user *src, u32 qsize, u32 in, u32 len); + +#define MAX_MEM_LOG_CNT 8192 +#define MAX_TRACE_SIZE 1024 + +struct mem_status { + /* Timestamp */ + struct timespec ts; + + /* Direction (TX or RX) */ + enum circ_dir_type dir; + + /* The status of memory interface at the time */ + u32 magic; + u32 access; + + u32 head[MAX_IPC_DEV][MAX_DIR]; + u32 tail[MAX_IPC_DEV][MAX_DIR]; + + u16 int2ap; + u16 int2cp; +}; + +struct mem_status_queue { + spinlock_t lock; + u32 in; + u32 out; + struct mem_status stat[MAX_MEM_LOG_CNT]; +}; + +struct circ_status { + u8 *buff; + u32 qsize; /* the size of a circular buffer */ + u32 in; + u32 out; + u32 size; /* the size of free space or received data */ +}; + +struct trace_data { + struct timespec ts; + enum dev_format dev; + struct circ_status circ_stat; + u8 *data; + u32 size; +}; + +struct trace_data_queue { + spinlock_t lock; + u32 in; + u32 out; + struct trace_data trd[MAX_TRACE_SIZE]; +}; + +struct mem_status *msq_get_free_slot(struct mem_status_queue *msq); +struct mem_status *msq_get_data_slot(struct mem_status_queue *msq); + +void print_mem_status(struct link_device *ld, struct mem_status *mst); +void print_circ_status(struct link_device *ld, int dev, struct mem_status *mst); +void print_ipc_trace(struct link_device *ld, int dev, struct circ_status *stat, + struct timespec *ts, u8 *buff, u32 rcvd); + +u8 *capture_mem_dump(struct link_device *ld, u8 *base, u32 size); +struct trace_data *trq_get_free_slot(struct trace_data_queue *trq); +struct trace_data *trq_get_data_slot(struct trace_data_queue *trq); + +#endif + diff --git a/drivers/misc/modem_if/modem_link_device_mipi.c b/drivers/misc/modem_if/modem_link_device_mipi.c index f2804e9..948a61c 100644 --- a/drivers/misc/modem_if/modem_link_device_mipi.c +++ b/drivers/misc/modem_if/modem_link_device_mipi.c @@ -26,7 +26,7 @@ #include <linux/semaphore.h> #include <linux/hsi_driver_if.h> -#include <linux/platform_data/modem.h> +#include "modem.h" #include "modem_prj.h" #include "modem_link_device_mipi.h" #include "modem_utils.h" diff --git a/drivers/misc/modem_if/modem_link_device_pld.c b/drivers/misc/modem_if/modem_link_device_pld.c index b68040e..c0769ff 100644 --- a/drivers/misc/modem_if/modem_link_device_pld.c +++ b/drivers/misc/modem_if/modem_link_device_pld.c @@ -25,190 +25,76 @@ #include <linux/if_arp.h> #include <linux/platform_device.h> #include <linux/kallsyms.h> -#include <linux/platform_data/modem.h> +#include "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); +static inline void clear_intr(struct pld_link_device *pld); +static inline u16 recv_intr(struct pld_link_device *pld); +static inline void send_intr(struct pld_link_device *pld, u16 mask); + +static inline u16 get_magic(struct pld_link_device *pld); +static inline void set_magic(struct pld_link_device *pld, u16 val); +static inline u16 get_access(struct pld_link_device *pld); +static inline void set_access(struct pld_link_device *pld, u16 val); + +static inline u32 get_tx_head(struct pld_link_device *pld, int id); +static inline u32 get_tx_tail(struct pld_link_device *pld, int id); +static inline void set_tx_head(struct pld_link_device *pld, int id, u32 in); +static inline void set_tx_tail(struct pld_link_device *pld, int id, u32 out); +static inline u8 *get_tx_buff(struct pld_link_device *pld, int id); +static inline u32 get_tx_buff_size(struct pld_link_device *pld, int id); + +static inline u32 get_rx_head(struct pld_link_device *pld, int id); +static inline u32 get_rx_tail(struct pld_link_device *pld, int id); +static inline void set_rx_head(struct pld_link_device *pld, int id, u32 in); +static inline void set_rx_tail(struct pld_link_device *pld, int id, u32 out); +static inline u8 *get_rx_buff(struct pld_link_device *pld, int id); +static inline u32 get_rx_buff_size(struct pld_link_device *pld, int id); + +static inline u16 get_mask_req_ack(struct pld_link_device *pld, int id); +static inline u16 get_mask_res_ack(struct pld_link_device *pld, int id); +static inline u16 get_mask_send(struct pld_link_device *pld, int id); + +static void handle_cp_crash(struct pld_link_device *pld); +static int trigger_force_cp_crash(struct pld_link_device *pld); /* ** 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, +static void set_dpram_map(struct pld_link_device *pld, 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; -} + map->magic = get_magic(pld); + map->access = get_access(pld); -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; -} + map->fmt_tx_in = get_tx_head(pld, IPC_FMT); + map->fmt_tx_out = get_tx_tail(pld, IPC_FMT); + map->fmt_rx_in = get_rx_head(pld, IPC_FMT); + map->fmt_rx_out = get_rx_tail(pld, IPC_FMT); + map->raw_tx_in = get_tx_head(pld, IPC_RAW); + map->raw_tx_out = get_tx_tail(pld, IPC_RAW); + map->raw_rx_in = get_rx_head(pld, IPC_RAW); + map->raw_rx_out = get_rx_tail(pld, IPC_RAW); -static inline void rxb_clear(struct dpram_rxb *rxb) -{ - rxb->data = NULL; - rxb->len = 0; + map->cp2ap = recv_intr(pld); } /* ** DPRAM operations */ -static int dpram_register_isr(unsigned irq, irqreturn_t (*isr)(int, void*), +static int pld_register_isr(unsigned irq, irqreturn_t (*isr)(int, void*), unsigned long flag, const char *name, - struct dpram_link_device *dpld) + struct pld_link_device *pld) { int ret = 0; - ret = request_irq(irq, isr, flag, name, dpld); + ret = request_irq(irq, isr, flag, name, pld); if (ret) { mif_info("%s: ERR! request_irq fail (err %d)\n", name, ret); return ret; @@ -223,31 +109,31 @@ static int dpram_register_isr(unsigned irq, irqreturn_t (*isr)(int, void*), return 0; } -static inline void clear_intr(struct dpram_link_device *dpld) +static inline void clear_intr(struct pld_link_device *pld) { - if (dpld->dpctl->clear_intr) - dpld->dpctl->clear_intr(); + if (pld->ext_op && pld->ext_op->clear_intr) + pld->ext_op->clear_intr(pld); } -static inline u16 recv_intr(struct dpram_link_device *dpld) +static inline u16 recv_intr(struct pld_link_device *pld) { u16 val1 = 0, val2 = 0, cnt = 3; unsigned long int flags; - spin_lock_irqsave(&dpld->pld_lock, flags); + spin_lock_irqsave(&pld->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(&pld->mbx2ap[0]), + pld->address_buffer); + val1 = ioread16(pld->base); - iowrite16(PLD_ADDR_MASK(&dpld->mbx2ap[0]), - dpld->address_buffer); - val2 = ioread16(dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&pld->mbx2ap[0]), + pld->address_buffer); + val2 = ioread16(pld->base); if (likely(val1 == val2)) { - spin_unlock_irqrestore(&dpld->pld_lock, flags); + spin_unlock_irqrestore(&pld->pld_lock, flags); return val1; } @@ -255,31 +141,31 @@ static inline u16 recv_intr(struct dpram_link_device *dpld) } while (cnt--); - spin_unlock_irqrestore(&dpld->pld_lock, flags); + spin_unlock_irqrestore(&pld->pld_lock, flags); return val1; } -static inline void send_intr(struct dpram_link_device *dpld, u16 mask) +static inline void send_intr(struct pld_link_device *pld, u16 mask) { int cnt = 3; u32 val = 0; unsigned long int flags; - spin_lock_irqsave(&dpld->pld_lock, flags); + spin_lock_irqsave(&pld->pld_lock, flags); - iowrite16(PLD_ADDR_MASK(&dpld->mbx2cp[0]), - dpld->address_buffer); - iowrite16((u16)mask, dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&pld->mbx2cp[0]), + pld->address_buffer); + iowrite16((u16)mask, pld->base); do { /* Check head value written */ - iowrite16(PLD_ADDR_MASK(&dpld->mbx2cp[0]), - dpld->address_buffer); - val = ioread16(dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&pld->mbx2cp[0]), + pld->address_buffer); + val = ioread16(pld->base); if (likely(val == mask)) { - spin_unlock_irqrestore(&dpld->pld_lock, flags); + spin_unlock_irqrestore(&pld->pld_lock, flags); return; } @@ -287,35 +173,35 @@ static inline void send_intr(struct dpram_link_device *dpld, u16 mask) udelay(100); /* Write head value again */ - iowrite16(PLD_ADDR_MASK(&dpld->mbx2cp[0]), - dpld->address_buffer); - iowrite16((u16)mask, dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&pld->mbx2cp[0]), + pld->address_buffer); + iowrite16((u16)mask, pld->base); } while (cnt--); - spin_unlock_irqrestore(&dpld->pld_lock, flags); + spin_unlock_irqrestore(&pld->pld_lock, flags); return; } -static inline u16 get_magic(struct dpram_link_device *dpld) +static inline u16 get_magic(struct pld_link_device *pld) { u16 val1 = 0, val2 = 0, cnt = 3; unsigned long int flags; - spin_lock_irqsave(&dpld->pld_lock, flags); + spin_lock_irqsave(&pld->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(&pld->magic_ap2cp[0]), + pld->address_buffer); + val1 = ioread16(pld->base); - iowrite16(PLD_ADDR_MASK(&dpld->magic_ap2cp[0]), - dpld->address_buffer); - val2 = ioread16(dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&pld->magic_ap2cp[0]), + pld->address_buffer); + val2 = ioread16(pld->base); if (likely(val1 == val2)) { - spin_unlock_irqrestore(&dpld->pld_lock, flags); + spin_unlock_irqrestore(&pld->pld_lock, flags); return val1; } @@ -324,31 +210,31 @@ static inline u16 get_magic(struct dpram_link_device *dpld) } while (cnt--); - spin_unlock_irqrestore(&dpld->pld_lock, flags); + spin_unlock_irqrestore(&pld->pld_lock, flags); return val1; } -static inline void set_magic(struct dpram_link_device *dpld, u16 in) +static inline void set_magic(struct pld_link_device *pld, u16 in) { int cnt = 3; u32 val = 0; unsigned long int flags; - spin_lock_irqsave(&dpld->pld_lock, flags); + spin_lock_irqsave(&pld->pld_lock, flags); - iowrite16(PLD_ADDR_MASK(&dpld->magic_ap2cp[0]), - dpld->address_buffer); - iowrite16((u16)in, dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&pld->magic_ap2cp[0]), + pld->address_buffer); + iowrite16((u16)in, pld->base); do { /* Check head value written */ - iowrite16(PLD_ADDR_MASK(&dpld->magic_ap2cp[0]), - dpld->address_buffer); - val = ioread16(dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&pld->magic_ap2cp[0]), + pld->address_buffer); + val = ioread16(pld->base); if (likely(val == in)) { - spin_unlock_irqrestore(&dpld->pld_lock, flags); + spin_unlock_irqrestore(&pld->pld_lock, flags); return; } @@ -356,34 +242,34 @@ static inline void set_magic(struct dpram_link_device *dpld, u16 in) udelay(100); /* Write head value again */ - iowrite16(PLD_ADDR_MASK(&dpld->magic_ap2cp[0]), - dpld->address_buffer); - iowrite16((u16)in, dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&pld->magic_ap2cp[0]), + pld->address_buffer); + iowrite16((u16)in, pld->base); } while (cnt--); - spin_unlock_irqrestore(&dpld->pld_lock, flags); + spin_unlock_irqrestore(&pld->pld_lock, flags); return; } -static inline u16 get_access(struct dpram_link_device *dpld) +static inline u16 get_access(struct pld_link_device *pld) { u16 val1 = 0, val2 = 0, cnt = 3; unsigned long int flags; - spin_lock_irqsave(&dpld->pld_lock, flags); + spin_lock_irqsave(&pld->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(&pld->access_ap2cp[0]), + pld->address_buffer); + val1 = ioread16(pld->base); - iowrite16(PLD_ADDR_MASK(&dpld->access_ap2cp[0]), - dpld->address_buffer); - val2 = ioread16(dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&pld->access_ap2cp[0]), + pld->address_buffer); + val2 = ioread16(pld->base); if (likely(val1 == val2)) { - spin_unlock_irqrestore(&dpld->pld_lock, flags); + spin_unlock_irqrestore(&pld->pld_lock, flags); return val1; } @@ -392,31 +278,31 @@ static inline u16 get_access(struct dpram_link_device *dpld) } while (cnt--); - spin_unlock_irqrestore(&dpld->pld_lock, flags); + spin_unlock_irqrestore(&pld->pld_lock, flags); return val1; } -static inline void set_access(struct dpram_link_device *dpld, u16 in) +static inline void set_access(struct pld_link_device *pld, 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); + iowrite16(PLD_ADDR_MASK(&pld->access_ap2cp[0]), + pld->address_buffer); + iowrite16((u16)in, pld->base); - spin_lock_irqsave(&dpld->pld_lock, flags); + spin_lock_irqsave(&pld->pld_lock, flags); do { /* Check head value written */ - iowrite16(PLD_ADDR_MASK(&dpld->access_ap2cp[0]), - dpld->address_buffer); - val = ioread16(dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&pld->access_ap2cp[0]), + pld->address_buffer); + val = ioread16(pld->base); if (likely(val == in)) { - spin_unlock_irqrestore(&dpld->pld_lock, flags); + spin_unlock_irqrestore(&pld->pld_lock, flags); return; } @@ -424,34 +310,34 @@ static inline void set_access(struct dpram_link_device *dpld, u16 in) udelay(100); /* Write head value again */ - iowrite16(PLD_ADDR_MASK(&dpld->access_ap2cp[0]), - dpld->address_buffer); - iowrite16((u16)in, dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&pld->access_ap2cp[0]), + pld->address_buffer); + iowrite16((u16)in, pld->base); } while (cnt--); - spin_unlock_irqrestore(&dpld->pld_lock, flags); + spin_unlock_irqrestore(&pld->pld_lock, flags); return; } -static inline u32 get_tx_head(struct dpram_link_device *dpld, int id) +static inline u32 get_tx_head(struct pld_link_device *pld, int id) { u16 val1 = 0, val2 = 0, cnt = 3; unsigned long int flags; - spin_lock_irqsave(&dpld->pld_lock, flags); + spin_lock_irqsave(&pld->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(&(pld->dev[id]->txq.head)[0]), + pld->address_buffer); + val1 = ioread16(pld->base); - iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->txq.head)[0]), - dpld->address_buffer); - val2 = ioread16(dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&(pld->dev[id]->txq.head)[0]), + pld->address_buffer); + val2 = ioread16(pld->base); if (likely(val1 == val2)) { - spin_unlock_irqrestore(&dpld->pld_lock, flags); + spin_unlock_irqrestore(&pld->pld_lock, flags); return val1; } @@ -461,29 +347,29 @@ static inline u32 get_tx_head(struct dpram_link_device *dpld, int id) } while (cnt--); - spin_unlock_irqrestore(&dpld->pld_lock, flags); + spin_unlock_irqrestore(&pld->pld_lock, flags); return val1; } -static inline u32 get_tx_tail(struct dpram_link_device *dpld, int id) +static inline u32 get_tx_tail(struct pld_link_device *pld, int id) { u16 val1 = 0, val2 = 0, cnt = 3; unsigned long int flags; - spin_lock_irqsave(&dpld->pld_lock, flags); + spin_lock_irqsave(&pld->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(&(pld->dev[id]->txq.tail)[0]), + pld->address_buffer); + val1 = ioread16(pld->base); - iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->txq.tail)[0]), - dpld->address_buffer); - val2 = ioread16(dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&(pld->dev[id]->txq.tail)[0]), + pld->address_buffer); + val2 = ioread16(pld->base); if (likely(val1 == val2)) { - spin_unlock_irqrestore(&dpld->pld_lock, flags); + spin_unlock_irqrestore(&pld->pld_lock, flags); return val1; } @@ -493,30 +379,30 @@ static inline u32 get_tx_tail(struct dpram_link_device *dpld, int id) } while (cnt--); - spin_unlock_irqrestore(&dpld->pld_lock, flags); + spin_unlock_irqrestore(&pld->pld_lock, flags); return val1; } -static inline void set_tx_head(struct dpram_link_device *dpld, int id, u32 in) +static inline void set_tx_head(struct pld_link_device *pld, int id, u32 in) { int cnt = 3; u32 val = 0; unsigned long int flags; - spin_lock_irqsave(&dpld->pld_lock, flags); + spin_lock_irqsave(&pld->pld_lock, flags); - iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->txq.head)[0]), - dpld->address_buffer); - iowrite16((u16)in, dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&(pld->dev[id]->txq.head)[0]), + pld->address_buffer); + iowrite16((u16)in, pld->base); do { /* Check head value written */ - iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->txq.head)[0]), - dpld->address_buffer); - val = ioread16(dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&(pld->dev[id]->txq.head)[0]), + pld->address_buffer); + val = ioread16(pld->base); if (likely(val == in)) { - spin_unlock_irqrestore(&dpld->pld_lock, flags); + spin_unlock_irqrestore(&pld->pld_lock, flags); return; } @@ -525,49 +411,49 @@ static inline void set_tx_head(struct dpram_link_device *dpld, int id, u32 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); + iowrite16(PLD_ADDR_MASK(&(pld->dev[id]->txq.head)[0]), + pld->address_buffer); + iowrite16((u16)in, pld->base); } while (cnt--); - spin_unlock_irqrestore(&dpld->pld_lock, flags); + spin_unlock_irqrestore(&pld->pld_lock, flags); return; } -static inline void set_tx_tail(struct dpram_link_device *dpld, int id, u32 out) +static inline void set_tx_tail(struct pld_link_device *pld, int id, u32 out) { return; } -static inline u8 *get_tx_buff(struct dpram_link_device *dpld, int id) +static inline u8 *get_tx_buff(struct pld_link_device *pld, int id) { - return dpld->dev[id]->txq.buff; + return pld->dev[id]->txq.buff; } -static inline u32 get_tx_buff_size(struct dpram_link_device *dpld, int id) +static inline u32 get_tx_buff_size(struct pld_link_device *pld, int id) { - return dpld->dev[id]->txq.size; + return pld->dev[id]->txq.size; } -static inline u32 get_rx_head(struct dpram_link_device *dpld, int id) +static inline u32 get_rx_head(struct pld_link_device *pld, int id) { u16 val1 = 0, val2 = 0, cnt = 3; unsigned long int flags; - spin_lock_irqsave(&dpld->pld_lock, flags); + spin_lock_irqsave(&pld->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(&(pld->dev[id]->rxq.head)[0]), + pld->address_buffer); + val1 = ioread16(pld->base); - iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->rxq.head)[0]), - dpld->address_buffer); - val2 = ioread16(dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&(pld->dev[id]->rxq.head)[0]), + pld->address_buffer); + val2 = ioread16(pld->base); if (likely(val1 == val2)) { - spin_unlock_irqrestore(&dpld->pld_lock, flags); + spin_unlock_irqrestore(&pld->pld_lock, flags); return val1; } @@ -577,29 +463,29 @@ static inline u32 get_rx_head(struct dpram_link_device *dpld, int id) } while (cnt--); - spin_unlock_irqrestore(&dpld->pld_lock, flags); + spin_unlock_irqrestore(&pld->pld_lock, flags); return val1; } -static inline u32 get_rx_tail(struct dpram_link_device *dpld, int id) +static inline u32 get_rx_tail(struct pld_link_device *pld, int id) { u16 val1 = 0, val2 = 0, cnt = 3; unsigned long int flags; - spin_lock_irqsave(&dpld->pld_lock, flags); + spin_lock_irqsave(&pld->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(&(pld->dev[id]->rxq.tail)[0]), + pld->address_buffer); + val1 = ioread16(pld->base); - iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->rxq.tail)[0]), - dpld->address_buffer); - val2 = ioread16(dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&(pld->dev[id]->rxq.tail)[0]), + pld->address_buffer); + val2 = ioread16(pld->base); if (likely(val1 == val2)) { - spin_unlock_irqrestore(&dpld->pld_lock, flags); + spin_unlock_irqrestore(&pld->pld_lock, flags); return val1; } @@ -609,35 +495,35 @@ static inline u32 get_rx_tail(struct dpram_link_device *dpld, int id) } while (cnt--); - spin_unlock_irqrestore(&dpld->pld_lock, flags); + spin_unlock_irqrestore(&pld->pld_lock, flags); return val1; } -static inline void set_rx_head(struct dpram_link_device *dpld, int id, u32 in) +static inline void set_rx_head(struct pld_link_device *pld, int id, u32 in) { return; } -static inline void set_rx_tail(struct dpram_link_device *dpld, int id, u32 out) +static inline void set_rx_tail(struct pld_link_device *pld, int id, u32 out) { int cnt = 3; u32 val = 0; unsigned long int flags; - spin_lock_irqsave(&dpld->pld_lock, flags); + spin_lock_irqsave(&pld->pld_lock, flags); - iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->rxq.tail)[0]), - dpld->address_buffer); - iowrite16((u16)out, dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&(pld->dev[id]->rxq.tail)[0]), + pld->address_buffer); + iowrite16((u16)out, pld->base); do { /* Check tail value written */ - iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->rxq.tail)[0]), - dpld->address_buffer); - val = ioread16(dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&(pld->dev[id]->rxq.tail)[0]), + pld->address_buffer); + val = ioread16(pld->base); if (val == out) { - spin_unlock_irqrestore(&dpld->pld_lock, flags); + spin_unlock_irqrestore(&pld->pld_lock, flags); return; } @@ -646,71 +532,60 @@ static inline void set_rx_tail(struct dpram_link_device *dpld, int id, u32 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); + iowrite16(PLD_ADDR_MASK(&(pld->dev[id]->rxq.tail)[0]), + pld->address_buffer); + iowrite16((u16)out, pld->base); } while (cnt--); - spin_unlock_irqrestore(&dpld->pld_lock, flags); + spin_unlock_irqrestore(&pld->pld_lock, flags); return; } -static inline u8 *get_rx_buff(struct dpram_link_device *dpld, int id) +static inline u8 *get_rx_buff(struct pld_link_device *pld, int id) { - return dpld->dev[id]->rxq.buff; + return pld->dev[id]->rxq.buff; } -static inline u32 get_rx_buff_size(struct dpram_link_device *dpld, int id) +static inline u32 get_rx_buff_size(struct pld_link_device *pld, int id) { - return dpld->dev[id]->rxq.size; + return pld->dev[id]->rxq.size; } -static inline u16 get_mask_req_ack(struct dpram_link_device *dpld, int id) +static inline u16 get_mask_req_ack(struct pld_link_device *pld, int id) { - return dpld->dev[id]->mask_req_ack; + return pld->dev[id]->mask_req_ack; } -static inline u16 get_mask_res_ack(struct dpram_link_device *dpld, int id) +static inline u16 get_mask_res_ack(struct pld_link_device *pld, int id) { - return dpld->dev[id]->mask_res_ack; + return pld->dev[id]->mask_res_ack; } -static inline u16 get_mask_send(struct dpram_link_device *dpld, int id) +static inline u16 get_mask_send(struct pld_link_device *pld, 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; + return pld->dev[id]->mask_send; } /* 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) +static inline int get_txq_space(struct pld_link_device *pld, int dev, u32 qsize, + u32 *in, u32 *out) { - struct link_device *ld = &dpld->ld; + struct link_device *ld = &pld->ld; int cnt = 3; u32 head; u32 tail; int space; do { - head = get_tx_head(dpld, dev); - tail = get_tx_tail(dpld, dev); + head = get_tx_head(pld, dev); + tail = get_tx_tail(pld, 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)) { + if (circ_valid(qsize, head, tail)) { *in = head; *out = tail; return space; @@ -729,27 +604,27 @@ static inline int dpram_get_txq_space(struct dpram_link_device *dpld, int dev, return -EINVAL; } -static void dpram_reset_tx_circ(struct dpram_link_device *dpld, int dev) +static void reset_tx_circ(struct pld_link_device *pld, int dev) { - set_tx_head(dpld, dev, 0); - set_tx_tail(dpld, dev, 0); + set_tx_head(pld, dev, 0); + set_tx_tail(pld, dev, 0); if (dev == IPC_FMT) - trigger_force_cp_crash(dpld); + trigger_force_cp_crash(pld); } /* 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) +static inline int get_rxq_rcvd(struct pld_link_device *pld, int dev, u32 qsize, + u32 *in, u32 *out) { - struct link_device *ld = &dpld->ld; + struct link_device *ld = &pld->ld; int cnt = 3; u32 head; u32 tail; u32 rcvd; do { - head = get_rx_head(dpld, dev); - tail = get_rx_tail(dpld, dev); + head = get_rx_head(pld, dev); + tail = get_rx_tail(pld, dev); if (head == tail) { *in = head; *out = tail; @@ -760,7 +635,7 @@ static inline int dpram_get_rxq_rcvd(struct dpram_link_device *dpld, int dev, 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)) { + if (circ_valid(qsize, head, tail)) { *in = head; *out = tail; return rcvd; @@ -779,54 +654,20 @@ static inline int dpram_get_rxq_rcvd(struct dpram_link_device *dpld, int dev, return -EINVAL; } -static void dpram_reset_rx_circ(struct dpram_link_device *dpld, int dev) +static void reset_rx_circ(struct pld_link_device *pld, int dev) { - set_rx_head(dpld, dev, 0); - set_rx_tail(dpld, dev, 0); + set_rx_head(pld, dev, 0); + set_rx_tail(pld, dev, 0); if (dev == IPC_FMT) - trigger_force_cp_crash(dpld); + trigger_force_cp_crash(pld); } -/* -** CAUTION : dpram_allow_sleep() MUST be invoked after dpram_wake_up() success -*/ -static int dpram_wake_up(struct dpram_link_device *dpld) +static int check_access(struct pld_link_device *pld) { - 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; + struct link_device *ld = &pld->ld; int i; - u16 magic = get_magic(dpld); - u16 access = get_access(dpld); + u16 magic = get_magic(pld); + u16 access = get_access(pld); if (likely(magic == DPRAM_MAGIC_CODE && access == 1)) return 0; @@ -836,8 +677,8 @@ static int dpram_check_access(struct dpram_link_device *dpld) ld->name, magic, access, i); udelay(100); - magic = get_magic(dpld); - access = get_access(dpld); + magic = get_magic(pld); + access = get_access(pld); if (likely(magic == DPRAM_MAGIC_CODE && access == 1)) return 0; } @@ -846,9 +687,9 @@ static int dpram_check_access(struct dpram_link_device *dpld) return -EACCES; } -static bool dpram_ipc_active(struct dpram_link_device *dpld) +static bool ipc_active(struct pld_link_device *pld) { - struct link_device *ld = &dpld->ld; + struct link_device *ld = &pld->ld; /* Check DPRAM mode */ if (ld->mode != LINK_MODE_IPC) { @@ -857,8 +698,8 @@ static bool dpram_ipc_active(struct dpram_link_device *dpld) return false; } - if (dpram_check_access(dpld) < 0) { - mif_info("%s: ERR! <%pf> dpram_check_access fail\n", + if (check_access(pld) < 0) { + mif_info("%s: ERR! <%pf> check_access fail\n", ld->name, __builtin_return_address(0)); return false; } @@ -866,67 +707,72 @@ static bool dpram_ipc_active(struct dpram_link_device *dpld) return true; } -static void dpram_ipc_write(struct dpram_link_device *dpld, int dev, +static void pld_ipc_write(struct pld_link_device *pld, 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); + struct link_device *ld = &pld->ld; + u8 __iomem *buff = get_tx_buff(pld, dev); u8 *src = skb->data; u32 len = skb->len; u32 inp; struct mif_irq_map map; + unsigned long int flags; + + spin_lock_irqsave(&pld->pld_lock, flags); if (in < out) { /* +++++++++ in ---------- out ++++++++++ */ - iowrite16(PLD_ADDR_MASK(&(buff+in)[0]), dpld->address_buffer); - memcpy(dpld->dp_base, src, len); + iowrite16(PLD_ADDR_MASK(&(buff+in)[0]), pld->address_buffer); + memcpy(pld->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)); + iowrite16(PLD_ADDR_MASK(&(buff+in)[0]), pld->address_buffer); + memcpy(pld->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)); + pld->address_buffer); + memcpy(pld->base, (src+space), (len-space)); } } + spin_unlock_irqrestore(&pld->pld_lock, flags); + /* update new in pointer */ inp = in + len; if (inp >= qsize) inp -= qsize; - set_tx_head(dpld, dev, inp); + set_tx_head(pld, dev, inp); if (dev == IPC_FMT) { - set_dpram_map(dpld, &map); + set_dpram_map(pld, &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) +static int pld_try_ipc_tx(struct pld_link_device *pld, int dev) { - struct link_device *ld = &dpld->ld; + struct link_device *ld = &pld->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 qsize = get_tx_buff_size(pld, dev); u32 in; u32 out; int space; int copied = 0; - spin_lock_irqsave(&dpld->tx_rx_lock, flags); + spin_lock_irqsave(&pld->tx_rx_lock, flags); while (1) { - space = dpram_get_txq_space(dpld, dev, qsize, &in, &out); + space = get_txq_space(pld, dev, qsize, &in, &out); if (unlikely(space < 0)) { - spin_unlock_irqrestore(&dpld->tx_rx_lock, flags); - dpram_reset_tx_circ(dpld, dev); + spin_unlock_irqrestore(&pld->tx_rx_lock, flags); + reset_tx_circ(pld, dev); return space; } @@ -935,9 +781,9 @@ static int dpram_try_ipc_tx(struct dpram_link_device *dpld, int dev) break; if (unlikely(space < skb->len)) { - atomic_set(&dpld->res_required[dev], 1); + atomic_set(&pld->res_required[dev], 1); skb_queue_head(txq, skb); - spin_unlock_irqrestore(&dpld->tx_rx_lock, flags); + spin_unlock_irqrestore(&pld->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), @@ -946,30 +792,30 @@ static int dpram_try_ipc_tx(struct dpram_link_device *dpld, int dev) } /* TX if there is enough room in the queue */ - dpram_ipc_write(dpld, dev, qsize, in, out, skb); + pld_ipc_write(pld, dev, qsize, in, out, skb); copied += skb->len; dev_kfree_skb_any(skb); } - spin_unlock_irqrestore(&dpld->tx_rx_lock, flags); + spin_unlock_irqrestore(&pld->tx_rx_lock, flags); return copied; } -static void dpram_ipc_rx_task(unsigned long data) +static void pld_ipc_rx_task(unsigned long data) { - struct dpram_link_device *dpld = (struct dpram_link_device *)data; - struct link_device *ld = &dpld->ld; + struct pld_link_device *pld = (struct pld_link_device *)data; + struct link_device *ld = &pld->ld; struct io_device *iod; - struct dpram_rxb *rxb; + struct mif_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]); + for (i = 0; i < ld->max_ipc_dev; i++) { + iod = pld->iod[i]; + qlen = rxbq_size(&pld->rxbq[i]); while (qlen > 0) { - rxb = rxbq_get_data_rxb(&dpld->rxbq[i]); + rxb = rxbq_get_data_rxb(&pld->rxbq[i]); iod->recv(iod, ld, rxb->data, rxb->len); rxb_clear(rxb); qlen--; @@ -977,36 +823,39 @@ static void dpram_ipc_rx_task(unsigned long data) } } -static void dpram_ipc_read(struct dpram_link_device *dpld, int dev, u8 *dst, +static void pld_ipc_read(struct pld_link_device *pld, int dev, u8 *dst, u8 __iomem *src, u32 out, u32 len, u32 qsize) { u8 *ori_det = dst; unsigned long flags; + spin_lock_irqsave(&pld->pld_lock, 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); + iowrite16(PLD_ADDR_MASK(&(src+out)[0]), pld->address_buffer); + memcpy(dst, pld->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); + iowrite16(PLD_ADDR_MASK(&(src+out)[0]), pld->address_buffer); + memcpy(dst, pld->base, len1); /* 2) buffer start -> in */ dst += len1; - iowrite16(PLD_ADDR_MASK(&src[0]), dpld->address_buffer); - memcpy(dst, dpld->dp_base, (len - len1)); + iowrite16(PLD_ADDR_MASK(&src[0]), pld->address_buffer); + memcpy(dst, pld->base, (len - len1)); } - if (dpld->ld.mode == LINK_MODE_IPC && ori_det[0] != 0x7F) { + spin_unlock_irqrestore(&pld->pld_lock, flags); + if (pld->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)); + get_rx_head(pld, dev), + get_rx_tail(pld, dev)); } } @@ -1016,190 +865,106 @@ 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_with_rxb(struct dpram_link_device *dpld, int dev) +static int pld_ipc_recv_data_with_rxb(struct pld_link_device *pld, 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); + struct link_device *ld = &pld->ld; + struct mif_rxb *rxb; + u8 __iomem *src = get_rx_buff(pld, dev); + u32 qsize = get_rx_buff_size(pld, dev); u32 in; u32 out; u32 rcvd; struct mif_irq_map map; unsigned long int flags; - spin_lock_irqsave(&dpld->tx_rx_lock, flags); + spin_lock_irqsave(&pld->tx_rx_lock, flags); - rcvd = dpram_get_rxq_rcvd(dpld, dev, qsize, &in, &out); + rcvd = get_rxq_rcvd(pld, dev, qsize, &in, &out); if (rcvd <= 0) { - spin_unlock_irqrestore(&dpld->tx_rx_lock, flags); + spin_unlock_irqrestore(&pld->tx_rx_lock, flags); return rcvd; } if (dev == IPC_FMT) { - set_dpram_map(dpld, &map); + set_dpram_map(pld, &map); mif_irq_log(ld->mc->msd, map, "ipc_recv", sizeof("ipc_recv")); } /* Allocate an rxb */ - rxb = rxbq_get_free_rxb(&dpld->rxbq[dev]); + rxb = rxbq_get_free_rxb(&pld->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); + spin_unlock_irqrestore(&pld->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); + pld_ipc_read(pld, 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); + set_rx_tail(pld, dev, out); + spin_unlock_irqrestore(&pld->tx_rx_lock, flags); return rcvd; } -static void non_command_handler(struct dpram_link_device *dpld, u16 non_cmd) +static void non_command_handler(struct pld_link_device *pld, u16 non_cmd) { - struct link_device *ld = &dpld->ld; + struct link_device *ld = &pld->ld; int i = 0; int ret = 0; - u16 tx_mask = 0; + u16 mask = 0; - if (!dpram_ipc_active(dpld)) + if (!ipc_active(pld)) 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); + for (i = 0; i < ld->max_ipc_dev; i++) { + ret = pld_ipc_recv_data_with_rxb(pld, i); if (ret < 0) - dpram_reset_rx_circ(dpld, i); + reset_rx_circ(pld, i); /* Check and process REQ_ACK (at this time, in == out) */ - if (non_cmd & get_mask_req_ack(dpld, i)) { + if (non_cmd & get_mask_req_ack(pld, i)) { mif_debug("%s: send %s_RES_ACK\n", ld->name, get_dev_name(i)); - tx_mask |= get_mask_res_ack(dpld, i); + mask |= get_mask_res_ack(pld, i); } } - if (!dpld->use_skb) { - /* Schedule soft IRQ for RX */ - tasklet_hi_schedule(&dpld->rx_tsk); - } + /* Schedule soft IRQ for RX */ + tasklet_hi_schedule(&pld->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); + for (i = 0; i < ld->max_ipc_dev; i++) { + if (atomic_read(&pld->res_required[i]) > 0) { + ret = pld_try_ipc_tx(pld, i); if (ret > 0) { - atomic_set(&dpld->res_required[i], 0); - tx_mask |= get_mask_send(dpld, i); + atomic_set(&pld->res_required[i], 0); + mask |= get_mask_send(pld, i); } else if (ret == -ENOSPC) { - tx_mask |= get_mask_req_ack(dpld, i); + mask |= get_mask_req_ack(pld, i); } } } - if (tx_mask) { - send_intr(dpld, INT_NON_CMD(tx_mask)); - mif_debug("%s: send intr 0x%04X\n", ld->name, tx_mask); + if (mask) { + send_intr(pld, INT_NON_CMD(mask)); + mif_debug("%s: send intr 0x%04X\n", ld->name, mask); } } -static void handle_cp_crash(struct dpram_link_device *dpld) +static void handle_cp_crash(struct pld_link_device *pld) { - struct link_device *ld = &dpld->ld; + struct link_device *ld = &pld->ld; struct io_device *iod; int i; - for (i = 0; i < dpld->max_ipc_dev; i++) { + for (i = 0; i < ld->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]); } @@ -1210,27 +975,27 @@ static void handle_cp_crash(struct dpram_link_device *dpld) 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); + iod = link_get_iod_with_channel(ld, RMNET0_CH_ID); 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; + struct pld_link_device *pld = (struct pld_link_device *)arg; + struct link_device *ld = &pld->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); + if (!wake_lock_active(&pld->wlock)) + wake_lock(&pld->wlock); - handle_cp_crash(dpld); + handle_cp_crash(pld); } -static int trigger_force_cp_crash(struct dpram_link_device *dpld) +static int trigger_force_cp_crash(struct pld_link_device *pld) { - struct link_device *ld = &dpld->ld; + struct link_device *ld = &pld->ld; if (ld->mode == LINK_MODE_ULOAD) { mif_err("%s: CP crash is already in progress\n", ld->mc->name); @@ -1240,79 +1005,72 @@ static int trigger_force_cp_crash(struct dpram_link_device *dpld) 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)); + send_intr(pld, INT_CMD(INT_CMD_CRASH_EXIT)); - mif_add_timer(&dpld->crash_ack_timer, FORCE_CRASH_ACK_TIMEOUT, - handle_no_crash_ack, (unsigned long)dpld); + mif_add_timer(&pld->crash_ack_timer, FORCE_CRASH_ACK_TIMEOUT, + handle_no_crash_ack, (unsigned long)pld); return 0; } -static int dpram_init_ipc(struct dpram_link_device *dpld) +static int pld_init_ipc(struct pld_link_device *pld) { - struct link_device *ld = &dpld->ld; + struct link_device *ld = &pld->ld; int i; if (ld->mode == LINK_MODE_IPC && - get_magic(dpld) == DPRAM_MAGIC_CODE && - get_access(dpld) == 1) + get_magic(pld) == DPRAM_MAGIC_CODE && + get_access(pld) == 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); + for (i = 0; i < ld->max_ipc_dev; i++) { + set_tx_head(pld, i, 0); + set_tx_tail(pld, i, 0); + set_rx_head(pld, i, 0); + set_rx_tail(pld, 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); + for (i = 0; i < ld->max_ipc_dev; i++) + pld->iod[i] = link_get_iod_with_format(ld, i); + pld->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 < ld->max_ipc_dev; i++) + atomic_set(&pld->res_required[i], 0); - 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); + spin_lock_init(&pld->tx_rx_lock); /* Enable IPC */ - atomic_set(&dpld->accessing, 0); + atomic_set(&pld->accessing, 0); - set_magic(dpld, DPRAM_MAGIC_CODE); - set_access(dpld, 1); - if (get_magic(dpld) != DPRAM_MAGIC_CODE || get_access(dpld) != 1) + set_magic(pld, DPRAM_MAGIC_CODE); + set_access(pld, 1); + if (get_magic(pld) != DPRAM_MAGIC_CODE || get_access(pld) != 1) return -EACCES; ld->mode = LINK_MODE_IPC; - if (wake_lock_active(&dpld->wlock)) - wake_unlock(&dpld->wlock); + if (wake_lock_active(&pld->wlock)) + wake_unlock(&pld->wlock); return 0; } -static void cmd_req_active_handler(struct dpram_link_device *dpld) +static void cmd_req_active_handler(struct pld_link_device *pld) { - send_intr(dpld, INT_CMD(INT_CMD_RES_ACTIVE)); + send_intr(pld, INT_CMD(INT_CMD_RES_ACTIVE)); } -static void cmd_crash_reset_handler(struct dpram_link_device *dpld) +static void cmd_crash_reset_handler(struct pld_link_device *pld) { - struct link_device *ld = &dpld->ld; + struct link_device *ld = &pld->ld; struct io_device *iod = NULL; ld->mode = LINK_MODE_ULOAD; - if (!wake_lock_active(&dpld->wlock)) - wake_lock(&dpld->wlock); + if (!wake_lock_active(&pld->wlock)) + wake_lock(&pld->wlock); mif_err("%s: Recv 0xC7 (CRASH_RESET)\n", ld->name); @@ -1323,35 +1081,33 @@ static void cmd_crash_reset_handler(struct dpram_link_device *dpld) iod->modem_state_changed(iod, STATE_CRASH_RESET); } -static void cmd_crash_exit_handler(struct dpram_link_device *dpld) +static void cmd_crash_exit_handler(struct pld_link_device *pld) { - struct link_device *ld = &dpld->ld; + struct link_device *ld = &pld->ld; ld->mode = LINK_MODE_ULOAD; - if (!wake_lock_active(&dpld->wlock)) - wake_lock(&dpld->wlock); + if (!wake_lock_active(&pld->wlock)) + wake_lock(&pld->wlock); mif_err("%s: Recv 0xC9 (CRASH_EXIT)\n", ld->name); - dpram_wake_up(dpld); - - del_timer(&dpld->crash_ack_timer); + del_timer(&pld->crash_ack_timer); - if (dpld->ext_op && dpld->ext_op->crash_log) - dpld->ext_op->crash_log(dpld); + if (pld->ext_op && pld->ext_op->crash_log) + pld->ext_op->crash_log(pld); - handle_cp_crash(dpld); + handle_cp_crash(pld); } -static void cmd_phone_start_handler(struct dpram_link_device *dpld) +static void cmd_phone_start_handler(struct pld_link_device *pld) { - struct link_device *ld = &dpld->ld; + struct link_device *ld = &pld->ld; struct io_device *iod = NULL; mif_info("%s: Recv 0xC8 (CP_START)\n", ld->name); - dpram_init_ipc(dpld); + pld_init_ipc(pld); iod = link_get_iod_with_format(ld, IPC_FMT); if (!iod) { @@ -1359,8 +1115,8 @@ static void cmd_phone_start_handler(struct dpram_link_device *dpld) return; } - if (dpld->ext_op && dpld->ext_op->cp_start_handler) - dpld->ext_op->cp_start_handler(dpld); + if (pld->ext_op && pld->ext_op->cp_start_handler) + pld->ext_op->cp_start_handler(pld); if (ld->mc->phone_state != STATE_ONLINE) { mif_info("%s: phone_state: %d -> ONLINE\n", @@ -1369,32 +1125,32 @@ static void cmd_phone_start_handler(struct dpram_link_device *dpld) } mif_info("%s: Send 0xC2 (INIT_END)\n", ld->name); - send_intr(dpld, INT_CMD(INT_CMD_INIT_END)); + send_intr(pld, INT_CMD(INT_CMD_INIT_END)); } -static void command_handler(struct dpram_link_device *dpld, u16 cmd) +static void command_handler(struct pld_link_device *pld, u16 cmd) { - struct link_device *ld = &dpld->ld; + struct link_device *ld = &pld->ld; switch (INT_CMD_MASK(cmd)) { case INT_CMD_REQ_ACTIVE: - cmd_req_active_handler(dpld); + cmd_req_active_handler(pld); break; case INT_CMD_CRASH_RESET: - dpld->dpram_init_status = DPRAM_INIT_STATE_NONE; - cmd_crash_reset_handler(dpld); + pld->init_status = PLD_INIT_STATE_NONE; + cmd_crash_reset_handler(pld); break; case INT_CMD_CRASH_EXIT: - dpld->dpram_init_status = DPRAM_INIT_STATE_NONE; - cmd_crash_exit_handler(dpld); + pld->init_status = PLD_INIT_STATE_NONE; + cmd_crash_exit_handler(pld); 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); + pld->init_status = PLD_INIT_STATE_READY; + cmd_phone_start_handler(pld); + complete_all(&pld->dpram_init_cmd); break; case INT_CMD_NV_REBUILDING: @@ -1402,14 +1158,14 @@ static void command_handler(struct dpram_link_device *dpld, u16 cmd) break; case INT_CMD_PIF_INIT_DONE: - complete_all(&dpld->modem_pif_init_done); + complete_all(&pld->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: + case INT_CMD_NORMAL_POWER_OFF: /*ToDo:*/ /*kernel_sec_set_cp_ack()*/; break; @@ -1424,123 +1180,46 @@ static void command_handler(struct dpram_link_device *dpld, u16 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) +static irqreturn_t pld_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; + struct pld_link_device *pld = (struct pld_link_device *)data; + struct link_device *ld = (struct link_device *)&pld->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); + int2ap = recv_intr(pld); 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); + if (pld->ext_op && pld->ext_op->dload_cmd_handler) { + pld->ext_op->dload_cmd_handler(pld, 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); - } + if (likely(INT_VALID(int2ap))) { + if (unlikely(INT_CMD_VALID(int2ap))) + command_handler(pld, int2ap); + else + non_command_handler(pld, 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); - } + mif_info("%s: ERR! invalid intr 0x%04X\n", + ld->name, int2ap); } exit: - clear_intr(dpld); - dpram_allow_sleep(dpld); + clear_intr(pld); return IRQ_HANDLED; } -static void dpram_send_ipc(struct link_device *ld, int dev, +static void pld_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 pld_link_device *pld = to_pld_link_device(ld); struct sk_buff_head *txq = ld->skb_txq[dev]; int ret; u16 mask; @@ -1551,46 +1230,31 @@ static void dpram_send_ipc(struct link_device *ld, int dev, 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)) + if (!ipc_active(pld)) goto exit; - if (atomic_read(&dpld->res_required[dev]) > 0) { + if (atomic_read(&pld->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); + ret = pld_try_ipc_tx(pld, dev); if (ret > 0) { - mask = get_mask_send(dpld, dev); - send_intr(dpld, INT_NON_CMD(mask)); + mask = get_mask_send(pld, dev); + send_intr(pld, INT_NON_CMD(mask)); } else if (ret == -ENOSPC) { - mask = get_mask_req_ack(dpld, dev); - send_intr(dpld, INT_NON_CMD(mask)); + mask = get_mask_req_ack(pld, dev); + send_intr(pld, 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); + mif_info("%s: pld_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; + return; } -static int dpram_send(struct link_device *ld, struct io_device *iod, +static int pld_send(struct link_device *ld, struct io_device *iod, struct sk_buff *skb) { enum dev_format dev = iod->format; @@ -1601,16 +1265,13 @@ static int dpram_send(struct link_device *ld, struct io_device *iod, case IPC_RAW: case IPC_RFS: if (likely(ld->mode == LINK_MODE_IPC)) { - dpram_send_ipc(ld, dev, iod, skb); + pld_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); @@ -1618,48 +1279,38 @@ static int dpram_send(struct link_device *ld, struct io_device *iod, } } -static int dpram_force_dump(struct link_device *ld, struct io_device *iod) +static int pld_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); + struct pld_link_device *pld = to_pld_link_device(ld); + trigger_force_cp_crash(pld); return 0; } -static void dpram_dump_memory(struct link_device *ld, char *buff) +static int pld_dump_start(struct link_device *ld, struct io_device *iod) { - struct dpram_link_device *dpld = to_dpram_link_device(ld); - u8 __iomem *base = dpld->dpctl->dp_base; - u32 size = dpld->dpctl->dp_size; + struct pld_link_device *pld = to_pld_link_device(ld); - 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); + if (pld->ext_op && pld->ext_op->dump_start) + return pld->ext_op->dump_start(pld); else return -ENODEV; } -static int dpram_dump_update(struct link_device *ld, struct io_device *iod, +static int pld_dump_update(struct link_device *ld, struct io_device *iod, unsigned long arg) { - struct dpram_link_device *dpld = to_dpram_link_device(ld); + struct pld_link_device *pld = to_pld_link_device(ld); - if (dpld->ext_op && dpld->ext_op->dump_update) - return dpld->ext_op->dump_update(dpld, (void *)arg); + if (pld->ext_op && pld->ext_op->dump_update) + return pld->ext_op->dump_update(pld, (void *)arg); else return -ENODEV; } -static int dpram_ioctl(struct link_device *ld, struct io_device *iod, +static int pld_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); + struct pld_link_device *pld = to_pld_link_device(ld); int err = 0; /* @@ -1669,11 +1320,11 @@ static int dpram_ioctl(struct link_device *ld, struct io_device *iod, switch (cmd) { case IOCTL_DPRAM_INIT_STATUS: mif_debug("%s: get dpram init status\n", ld->name); - return dpld->dpram_init_status; + return pld->init_status; default: - if (dpld->ext_ioctl) { - err = dpld->ext_ioctl(dpld, iod, cmd, arg); + if (pld->ext_ioctl) { + err = pld->ext_ioctl(pld, iod, cmd, arg); } else { mif_err("%s: ERR! invalid cmd 0x%08X\n", ld->name, cmd); err = -EINVAL; @@ -1685,97 +1336,97 @@ static int dpram_ioctl(struct link_device *ld, struct io_device *iod, return err; } -static int dpram_table_init(struct dpram_link_device *dpld) +static int pld_table_init(struct pld_link_device *pld) { - struct link_device *ld = &dpld->ld; + struct link_device *ld = &pld->ld; u8 __iomem *dp_base; int i; - if (!dpld->dp_base) { - mif_info("%s: ERR! dpld->dp_base == NULL\n", ld->name); + if (!pld->base) { + mif_info("%s: ERR! pld->base == NULL\n", ld->name); return -EINVAL; } - dp_base = dpld->dp_base; + dp_base = pld->base; /* Map for IPC */ - if (dpld->dpctl->ipc_map) { - memcpy(&dpld->ipc_map, dpld->dpctl->ipc_map, - sizeof(struct dpram_ipc_map)); + if (pld->dpram->ipc_map) { + memcpy(&pld->ipc_map, pld->dpram->ipc_map, + sizeof(struct pld_ipc_map)); } - dpld->magic_ap2cp = dpld->ipc_map.magic_ap2cp; - dpld->access_ap2cp = dpld->ipc_map.access_ap2cp; + pld->magic_ap2cp = pld->ipc_map.magic_ap2cp; + pld->access_ap2cp = pld->ipc_map.access_ap2cp; - dpld->magic_cp2ap = dpld->ipc_map.magic_cp2ap; - dpld->access_cp2ap = dpld->ipc_map.access_cp2ap; + pld->magic_cp2ap = pld->ipc_map.magic_cp2ap; + pld->access_cp2ap = pld->ipc_map.access_cp2ap; - dpld->address_buffer = dpld->ipc_map.address_buffer; + pld->address_buffer = pld->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; + for (i = 0; i < ld->max_ipc_dev; i++) + pld->dev[i] = &pld->ipc_map.dev[i]; + pld->mbx2ap = pld->ipc_map.mbx_cp2ap; + pld->mbx2cp = pld->ipc_map.mbx_ap2cp; /* Map for booting */ - if (dpld->ext_op && dpld->ext_op->init_boot_map) { - dpld->ext_op->init_boot_map(dpld); + if (pld->ext_op && pld->ext_op->init_boot_map) { + pld->ext_op->init_boot_map(pld); } 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; + pld->bt_map.magic = (u32 *)(dp_base); + pld->bt_map.buff = (u8 *)(dp_base + DP_BOOT_BUFF_OFFSET); + pld->bt_map.space = pld->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); + if (pld->ext_op && pld->ext_op->init_dl_map) { + pld->ext_op->init_dl_map(pld); } else { - dpld->dl_map.magic = (u32 *)(dp_base); - dpld->dl_map.buff = (u8 *)(dp_base + DP_DLOAD_BUFF_OFFSET); + pld->dl_map.magic = (u32 *)(dp_base); + pld->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); + if (pld->ext_op && pld->ext_op->init_ul_map) { + pld->ext_op->init_ul_map(pld); } else { - dpld->ul_map.magic = (u32 *)(dp_base); - dpld->ul_map.buff = (u8 *)(dp_base + DP_ULOAD_BUFF_OFFSET); + pld->ul_map.magic = (u32 *)(dp_base); + pld->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) +static void pld_setup_common_op(struct pld_link_device *pld) +{ + pld->clear_intr = clear_intr; + pld->recv_intr = recv_intr; + pld->send_intr = send_intr; + pld->get_magic = get_magic; + pld->set_magic = set_magic; + pld->get_access = get_access; + pld->set_access = set_access; + pld->get_tx_head = get_tx_head; + pld->get_tx_tail = get_tx_tail; + pld->set_tx_head = set_tx_head; + pld->set_tx_tail = set_tx_tail; + pld->get_tx_buff = get_tx_buff; + pld->get_tx_buff_size = get_tx_buff_size; + pld->get_rx_head = get_rx_head; + pld->get_rx_tail = get_rx_tail; + pld->set_rx_head = set_rx_head; + pld->set_rx_tail = set_rx_tail; + pld->get_rx_buff = get_rx_buff; + pld->get_rx_buff_size = get_rx_buff_size; + pld->get_mask_req_ack = get_mask_req_ack; + pld->get_mask_res_ack = get_mask_res_ack; + pld->get_mask_send = get_mask_send; +} + +static int pld_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) +static void pld_link_terminate(struct link_device *ld, struct io_device *iod) { return; } @@ -1783,11 +1434,11 @@ static void dpram_link_terminate(struct link_device *ld, struct io_device *iod) struct link_device *pld_create_link_device(struct platform_device *pdev) { struct modem_data *mdm_data = NULL; - struct dpram_link_device *dpld = NULL; + struct pld_link_device *pld = NULL; struct link_device *ld = NULL; struct resource *res = NULL; resource_size_t res_size; - struct modemlink_dpram_control *dpctl = NULL; + struct modemlink_dpram_data *dpram = NULL; unsigned long task_data; int ret = 0; int i = 0; @@ -1803,19 +1454,19 @@ struct link_device *pld_create_link_device(struct platform_device *pdev) 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"); + if (!mdm_data->dpram) { + mif_info("ERR! no mdm_data->dpram\n"); goto err; } - dpctl = mdm_data->dpram_ctl; + dpram = mdm_data->dpram; /* Alloc DPRAM link device structure */ - dpld = kzalloc(sizeof(struct dpram_link_device), GFP_KERNEL); - if (!dpld) { - mif_info("ERR! kzalloc dpld fail\n"); + pld = kzalloc(sizeof(struct pld_link_device), GFP_KERNEL); + if (!pld) { + mif_info("ERR! kzalloc pld fail\n"); goto err; } - ld = &dpld->ld; + ld = &pld->ld; /* Retrieve modem data and DPRAM control data from the modem data */ ld->mdm_data = mdm_data; @@ -1823,30 +1474,30 @@ struct link_device *pld_create_link_device(struct platform_device *pdev) 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; + pld->dpram = dpram; + pld->type = dpram->type; if (mdm_data->ipc_version < SIPC_VER_50) { - if (!dpctl->max_ipc_dev) { + if (!mdm_data->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; + ld->aligned = dpram->aligned; + ld->max_ipc_dev = mdm_data->max_ipc_dev; } else { ld->aligned = 1; - dpld->max_ipc_dev = MAX_SIPC5_DEV; + ld->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; + ld->init_comm = pld_link_init; + ld->terminate_comm = pld_link_terminate; + ld->send = pld_send; + ld->force_dump = pld_force_dump; + ld->dump_start = pld_dump_start; + ld->dump_update = pld_dump_update; + ld->ioctl = pld_ioctl; INIT_LIST_HEAD(&ld->list); @@ -1858,14 +1509,13 @@ struct link_device *pld_create_link_device(struct platform_device *pdev) 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; + pld_setup_common_op(pld); + pld->ext_op = pld_get_ext_op(mdm_data->modem_type); + if (pld->ext_op && pld->ext_op->ioctl) + pld->ext_ioctl = pld->ext_op->ioctl; /* Retrieve DPRAM resource */ - if (!dpctl->dp_base) { + if (!dpram->base) { res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { mif_info("%s: ERR! platform_get_resource fail\n", @@ -1874,59 +1524,54 @@ struct link_device *pld_create_link_device(struct platform_device *pdev) } res_size = resource_size(res); - dpctl->dp_base = ioremap_nocache(res->start, res_size); - dpctl->dp_size = res_size; + dpram->base = ioremap_nocache(res->start, res_size); + dpram->size = res_size; } - dpld->dp_base = dpctl->dp_base; - dpld->dp_size = dpctl->dp_size; + pld->base = dpram->base; + pld->size = dpram->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); + mif_info("%s: type %d, aligned %d, base 0x%08X, size %d\n", + ld->name, pld->type, ld->aligned, (int)pld->base, pld->size); /* Initialize DPRAM map (physical map -> logical map) */ - ret = dpram_table_init(dpld); + ret = pld_table_init(pld); if (ret < 0) { - mif_info("%s: ERR! dpram_table_init fail (err %d)\n", + mif_info("%s: ERR! pld_table_init fail (err %d)\n", ld->name, ret); goto err; } - spin_lock_init(&dpld->pld_lock); + spin_lock_init(&pld->pld_lock); /* Disable IPC */ - set_magic(dpld, 0); - set_access(dpld, 0); + set_magic(pld, 0); + set_access(pld, 0); - dpld->dpram_init_status = DPRAM_INIT_STATE_NONE; + pld->init_status = PLD_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); + snprintf(pld->wlock_name, MIF_MAX_NAME_LEN, "%s_wlock", ld->name); + wake_lock_init(&pld->wlock, WAKE_LOCK_SUSPEND, pld->wlock_name); - task_data = (unsigned long)dpld; - tasklet_init(&dpld->rx_tsk, dpram_ipc_rx_task, task_data); + init_completion(&pld->dpram_init_cmd); + init_completion(&pld->modem_pif_init_done); + init_completion(&pld->udl_start_complete); + init_completion(&pld->udl_cmd_complete); + init_completion(&pld->crash_start_complete); + init_completion(&pld->crash_recv_done); - /* Prepare SKB queue head for RX processing */ - for (i = 0; i < dpld->max_ipc_dev; i++) - skb_queue_head_init(&dpld->skb_rxq[i]); + task_data = (unsigned long)pld; + tasklet_init(&pld->rx_tsk, pld_ipc_rx_task, task_data); /* 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) { + qsize = MAX_RXBQ_SIZE; + for (i = 0; i < ld->max_ipc_dev; i++) { + bsize = rxbq_get_page_size(get_rx_buff_size(pld, i)); + pld->rxbq[i].size = qsize; + pld->rxbq[i].in = 0; + pld->rxbq[i].out = 0; + pld->rxbq[i].rxb = rxbq_create_pool(bsize, qsize); + if (!pld->rxbq[i].rxb) { mif_info("%s: ERR! %s rxbq_create_pool fail\n", ld->name, get_dev_name(i)); goto err; @@ -1936,44 +1581,37 @@ struct link_device *pld_create_link_device(struct platform_device *pdev) } /* 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); + pld->buff = kzalloc(pld->size, GFP_KERNEL); + if (!pld->buff) { + mif_info("%s: ERR! kzalloc pld->buff fail\n", ld->name); goto err; } /* Retrieve DPRAM IRQ GPIO# */ - dpld->gpio_dpram_int = mdm_data->gpio_dpram_int; + pld->gpio_ipc_int2ap = mdm_data->gpio_ipc_int2ap; /* 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; + pld->irq = mdm_data->irq_ipc_int2ap; /* 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; + if (mdm_data->irqf_ipc_int2ap) + pld->irq_flags = mdm_data->irqf_ipc_int2ap; + else + pld->irq_flags = (IRQF_NO_SUSPEND | IRQF_TRIGGER_LOW); /* 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); + snprintf(pld->irq_name, MIF_MAX_NAME_LEN, "%s_irq", ld->name); + ret = pld_register_isr(pld->irq, pld_irq_handler, pld->irq_flags, + pld->irq_name, pld); if (ret) goto err; return ld; err: - if (dpld) { - kfree(dpld->buff); - kfree(dpld); + if (pld) { + kfree(pld->buff); + kfree(pld); } return NULL; diff --git a/drivers/misc/modem_if/modem_link_device_pld.h b/drivers/misc/modem_if/modem_link_device_pld.h index 2656110..2690faa 100644 --- a/drivers/misc/modem_if/modem_link_device_pld.h +++ b/drivers/misc/modem_if/modem_link_device_pld.h @@ -1,5 +1,4 @@ /* - * Copyright (C) 2011 Google, Inc. * Copyright (C) 2010 Samsung Electronics. * * This software is licensed under the terms of the GNU General Public @@ -12,181 +11,134 @@ * 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 */ +#ifndef __MODEM_LINK_DEVICE_PLD_H__ +#define __MODEM_LINK_DEVICE_PLD_H__ -#define PLD_ADDR_MASK(x) (0x00003FFF & (unsigned long)(x)) +#include "modem_link_device_memory.h" -enum host_boot_mode { - HOST_BOOT_MODE_NORMAL, - HOST_BOOT_MODE_DUMP, -}; +#define PLD_ADDR_MASK(x) (0x00003FFF & (unsigned long)(x)) -enum dpram_init_status { - DPRAM_INIT_STATE_NONE, - DPRAM_INIT_STATE_READY, +enum pld_init_status { + PLD_INIT_STATE_NONE, + PLD_INIT_STATE_READY, }; -struct dpram_boot_img { - char *addr; - int size; - enum host_boot_mode mode; - unsigned req; - unsigned resp; -}; +#if 1 +#define MAX_RXBQ_SIZE 256 -#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]; -}; +struct mif_rxb { + u8 *buff; + unsigned 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 */ + u8 *data; + unsigned len; }; -struct dpram_firmware { - char *firmware; +struct mif_rxb_queue { 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, + int in; + int out; + struct mif_rxb *rxb; }; -struct dpram_boot_map { - u32 __iomem *magic; - u8 __iomem *buff; - u32 __iomem *req; - u32 __iomem *resp; - u32 size; -}; +/* +** RXB (DPRAM RX buffer) functions +*/ +static inline struct mif_rxb *rxbq_create_pool(unsigned size, int count) +{ + struct mif_rxb *rxb; + u8 *buff; + int i; + + rxb = kzalloc(sizeof(struct mif_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 mif_rxb_queue *rxbq) +{ + return (rxbq->in == rxbq->out) ? true : false; +} + +static inline int rxbq_free_size(struct mif_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 mif_rxb *rxbq_get_free_rxb(struct mif_rxb_queue *rxbq) +{ + struct mif_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 mif_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 mif_rxb *rxbq_get_data_rxb(struct mif_rxb_queue *rxbq) +{ + struct mif_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 mif_rxb *rxb, unsigned len) +{ + rxb->len = len; + return rxb->data; +} + +static inline void rxb_clear(struct mif_rxb *rxb) +{ + rxb->data = NULL; + rxb->len = 0; +} +#endif struct qc_dpram_boot_map { u8 __iomem *buff; @@ -195,39 +147,14 @@ struct qc_dpram_boot_map { 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 { +struct qc_dpram_udl_param { unsigned char *addr; unsigned int size; unsigned int count; unsigned int tag; }; -struct dpram_udl_check { +struct qc_dpram_udl_check { unsigned int total_size; unsigned int rest_size; unsigned int send_size; @@ -236,180 +163,38 @@ struct dpram_udl_check { 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; +struct pld_ext_op; - 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 pld_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 */ + enum dpram_type type; /* DPRAM type */ + u8 __iomem *base; /* DPRAM base virtual address */ + u32 size; /* DPRAM size */ /* DPRAM IRQ GPIO# */ - unsigned gpio_dpram_int; + unsigned gpio_ipc_int2ap; /* DPRAM IRQ from CP */ int irq; unsigned long irq_flags; - char irq_name[DP_MAX_NAME_LEN]; + char irq_name[MIF_MAX_NAME_LEN]; /* Link to DPRAM control functions dependent on each platform */ - int max_ipc_dev; - struct modemlink_dpram_control *dpctl; + struct modemlink_dpram_data *dpram; /* Physical configuration -> logical configuration */ union { - struct dpram_boot_map bt_map; + struct memif_boot_map bt_map; struct qc_dpram_boot_map qc_bt_map; }; - struct dpram_dload_map dl_map; - struct dpram_uload_map ul_map; + struct memif_dload_map dl_map; + struct memif_uload_map ul_map; /* IPC device map */ - struct dpram_ipc_map ipc_map; + struct pld_ipc_map ipc_map; /* Pointers (aliases) to IPC device map */ u16 __iomem *magic_ap2cp; @@ -424,7 +209,7 @@ struct dpram_link_device { /* Wakelock for DPRAM device */ struct wake_lock wlock; - char wlock_name[DP_MAX_NAME_LEN]; + char wlock_name[MIF_MAX_NAME_LEN]; /* For booting */ unsigned boot_start_complete; @@ -432,19 +217,16 @@ struct dpram_link_device { 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; + struct qc_dpram_udl_check qc_udl_check; + struct qc_dpram_udl_param qc_udl_param; - /* For CP RAM dump */ + /* For CP crash 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 */ + struct completion crash_start_complete; + struct completion crash_recv_done; /* For locking TX process */ spinlock_t tx_rx_lock; @@ -452,10 +234,8 @@ struct dpram_link_device { /* For efficient RX process */ struct tasklet_struct rx_tsk; - struct dpram_rxb_queue rxbq[MAX_IPC_DEV]; + struct mif_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]; @@ -466,67 +246,65 @@ struct dpram_link_device { /* Multi-purpose miscellaneous buffer */ u8 *buff; - /* DPRAM IPC initialization status */ - int dpram_init_status; + /* PLD IPC initialization status */ + int init_status; /* Alias to device-specific IOCTL function */ - int (*ext_ioctl)(struct dpram_link_device *dpld, struct io_device *iod, + int (*ext_ioctl)(struct pld_link_device *pld, 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); + void (*clear_intr)(struct pld_link_device *pld); + u16 (*recv_intr)(struct pld_link_device *pld); + void (*send_intr)(struct pld_link_device *pld, u16 mask); + u16 (*get_magic)(struct pld_link_device *pld); + void (*set_magic)(struct pld_link_device *pld, u16 value); + u16 (*get_access)(struct pld_link_device *pld); + void (*set_access)(struct pld_link_device *pld, u16 value); + u32 (*get_tx_head)(struct pld_link_device *pld, int id); + u32 (*get_tx_tail)(struct pld_link_device *pld, int id); + void (*set_tx_head)(struct pld_link_device *pld, int id, u32 head); + void (*set_tx_tail)(struct pld_link_device *pld, int id, u32 tail); + u8 *(*get_tx_buff)(struct pld_link_device *pld, int id); + u32 (*get_tx_buff_size)(struct pld_link_device *pld, int id); + u32 (*get_rx_head)(struct pld_link_device *pld, int id); + u32 (*get_rx_tail)(struct pld_link_device *pld, int id); + void (*set_rx_head)(struct pld_link_device *pld, int id, u32 head); + void (*set_rx_tail)(struct pld_link_device *pld, int id, u32 tail); + u8 *(*get_rx_buff)(struct pld_link_device *pld, int id); + u32 (*get_rx_buff_size)(struct pld_link_device *pld, int id); + u16 (*get_mask_req_ack)(struct pld_link_device *pld, int id); + u16 (*get_mask_res_ack)(struct pld_link_device *pld, int id); + u16 (*get_mask_send)(struct pld_link_device *pld, int id); /* Extended operations for various modems */ - struct dpram_ext_op *ext_op; + struct pld_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) +#define to_pld_link_device(linkdev) \ + container_of(linkdev, struct pld_link_device, ld) -struct dpram_ext_op { +struct pld_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); + void (*init_boot_map)(struct pld_link_device *pld); + void (*init_dl_map)(struct pld_link_device *pld); + void (*init_ul_map)(struct pld_link_device *pld); - int (*dload_bin)(struct dpram_link_device *dpld, struct sk_buff *skb); - void (*dload_cmd_handler)(struct dpram_link_device *dpld, u16 cmd); + void (*dload_cmd_handler)(struct pld_link_device *pld, u16 cmd); - void (*cp_start_handler)(struct dpram_link_device *dpld); + void (*cp_start_handler)(struct pld_link_device *pld); - 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); + void (*crash_log)(struct pld_link_device *pld); + int (*dump_start)(struct pld_link_device *pld); + int (*dump_update)(struct pld_link_device *pld, void *arg); - int (*ioctl)(struct dpram_link_device *dpld, struct io_device *iod, + int (*ioctl)(struct pld_link_device *pld, struct io_device *iod, unsigned int cmd, unsigned long arg); + + void (*clear_intr)(struct pld_link_device *pld); }; -struct dpram_ext_op *dpram_get_ext_op(enum modem_t modem); +struct pld_ext_op *pld_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 index ae6578c..26b0e28 100644 --- a/drivers/misc/modem_if/modem_link_device_pld_ext_op.c +++ b/drivers/misc/modem_if/modem_link_device_pld_ext_op.c @@ -25,8 +25,8 @@ #include <linux/if_arp.h> #include <linux/platform_device.h> #include <linux/kallsyms.h> -#include <linux/platform_data/modem.h> +#include "modem.h" #include "modem_prj.h" #include "modem_link_device_pld.h" #include "modem_utils.h" @@ -41,56 +41,55 @@ enum qc_dload_tag { static void qc_dload_task(unsigned long data); -static void qc_init_boot_map(struct dpram_link_device *dpld) +static void qc_init_boot_map(struct pld_link_device *pld) { - struct qc_dpram_boot_map *bt_map = &dpld->qc_bt_map; - struct modemlink_dpram_control *dpctl = dpld->dpctl; + struct qc_dpram_boot_map *qbt_map = &pld->qc_bt_map; + struct modemlink_dpram_data *dpram = pld->dpram; - 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); + qbt_map->buff = pld->dev[0]->txq.buff; + qbt_map->frame_size = (u16 *)(pld->base + dpram->boot_size_offset); + qbt_map->tag = (u16 *)(pld->base + dpram->boot_tag_offset); + qbt_map->count = (u16 *)(pld->base + dpram->boot_count_offset); - tasklet_init(&dpld->dl_tsk, qc_dload_task, (unsigned long)dpld); + tasklet_init(&pld->dl_tsk, qc_dload_task, (unsigned long)pld); } -static void qc_dload_map(struct dpram_link_device *dpld, u8 is_upload) +static void qc_dload_map(struct pld_link_device *pld, u8 is_upload) { - struct qc_dpram_boot_map *bt_map = &dpld->qc_bt_map; - struct modemlink_dpram_control *dpctl = dpld->dpctl; + struct qc_dpram_boot_map *qbt_map = &pld->qc_bt_map; + struct modemlink_dpram_data *dpram = pld->dpram; unsigned int upload_offset = 0; if (is_upload == 1) { upload_offset = 0x1000; - bt_map->buff = dpld->dev[0]->rxq.buff; + qbt_map->buff = pld->dev[0]->rxq.buff; } else { upload_offset = 0; - bt_map->buff = dpld->dev[0]->txq.buff; + qbt_map->buff = pld->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); - + qbt_map->frame_size = (u16 *)(pld->base + + dpram->boot_size_offset + upload_offset); + qbt_map->tag = (u16 *)(pld->base + + dpram->boot_tag_offset + upload_offset); + qbt_map->count = (u16 *)(pld->base + + dpram->boot_count_offset + upload_offset); } -static int qc_prepare_download(struct dpram_link_device *dpld) +static int qc_prepare_download(struct pld_link_device *pld) { int retval = 0; int count = 0; - qc_dload_map(dpld, 0); + qc_dload_map(pld, 0); while (1) { - if (dpld->udl_check.copy_start) { - dpld->udl_check.copy_start = 0; + if (pld->qc_udl_check.copy_start) { + pld->qc_udl_check.copy_start = 0; break; } - msleep(20); + usleep_range(10000, 11000); count++; if (count > 1000) { @@ -102,42 +101,42 @@ static int qc_prepare_download(struct dpram_link_device *dpld) return retval; } -static void _qc_do_download(struct dpram_link_device *dpld, - struct dpram_udl_param *param) +static void _qc_do_download(struct pld_link_device *pld, + struct qc_dpram_udl_param *param) { - struct qc_dpram_boot_map *bt_map = &dpld->qc_bt_map; + struct qc_dpram_boot_map *qbt_map = &pld->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); + if (param->size <= pld->dpram->max_boot_frame_size) { + iowrite16(PLD_ADDR_MASK(&qbt_map->buff[0]), + pld->address_buffer); + memcpy(pld->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(&qbt_map->frame_size[0]), + pld->address_buffer); + iowrite16(param->size, pld->base); - iowrite16(PLD_ADDR_MASK(&bt_map->tag[0]), - dpld->address_buffer); - iowrite16(param->tag, dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&qbt_map->tag[0]), + pld->address_buffer); + iowrite16(param->tag, pld->base); - iowrite16(PLD_ADDR_MASK(&bt_map->count[0]), - dpld->address_buffer); - iowrite16(param->count, dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&qbt_map->count[0]), + pld->address_buffer); + iowrite16(param->count, pld->base); - dpld->send_intr(dpld, 0xDB12); + pld->send_intr(pld, 0xDB12); } else { mif_info("param->size %d\n", param->size); } } -static int _qc_download(struct dpram_link_device *dpld, void *arg, +static int _qc_download(struct pld_link_device *pld, void *arg, enum qc_dload_tag tag) { int retval = 0; int count = 0; int cnt_limit; unsigned char *img; - struct dpram_udl_param param; + struct qc_dpram_udl_param param; retval = copy_from_user((void *)¶m, (void *)arg, sizeof(param)); if (retval < 0) { @@ -153,24 +152,24 @@ static int _qc_download(struct dpram_link_device *dpld, void *arg, 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; + pld->qc_udl_check.total_size = param.size; + pld->qc_udl_check.rest_size = param.size; + pld->qc_udl_check.send_size = 0; + pld->qc_udl_check.copy_complete = 0; - dpld->udl_param.addr = img; - dpld->udl_param.size = dpld->dpctl->max_boot_frame_size; + pld->qc_udl_param.addr = img; + pld->qc_udl_param.size = pld->dpram->max_boot_frame_size; if (tag == QC_DLOAD_TAG_NV) - dpld->udl_param.count = 1; + pld->qc_udl_param.count = 1; else - dpld->udl_param.count = param.count; - dpld->udl_param.tag = tag; + pld->qc_udl_param.count = param.count; + pld->qc_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; + if (pld->qc_udl_check.rest_size < pld->dpram->max_boot_frame_size) + pld->qc_udl_param.size = pld->qc_udl_check.rest_size; /* Download image (binary or NV) */ - _qc_do_download(dpld, &dpld->udl_param); + _qc_do_download(pld, &pld->qc_udl_param); /* Wait for completion */ @@ -180,13 +179,13 @@ static int _qc_download(struct dpram_link_device *dpld, void *arg, cnt_limit = 1000; while (1) { - if (dpld->udl_check.copy_complete) { - dpld->udl_check.copy_complete = 0; + if (pld->qc_udl_check.copy_complete) { + pld->qc_udl_check.copy_complete = 0; retval = 0; break; } - msleep(20); + usleep_range(10000, 11000); count++; if (count > cnt_limit) { @@ -201,53 +200,53 @@ static int _qc_download(struct dpram_link_device *dpld, void *arg, return retval; } -static int qc_download_bin(struct dpram_link_device *dpld, void *arg) +static int qc_download_bin(struct pld_link_device *pld, void *arg) { - return _qc_download(dpld, arg, QC_DLOAD_TAG_BIN); + return _qc_download(pld, arg, QC_DLOAD_TAG_BIN); } -static int qc_download_nv(struct dpram_link_device *dpld, void *arg) +static int qc_download_nv(struct pld_link_device *pld, void *arg) { - return _qc_download(dpld, arg, QC_DLOAD_TAG_NV); + return _qc_download(pld, arg, QC_DLOAD_TAG_NV); } static void qc_dload_task(unsigned long data) { - struct dpram_link_device *dpld = (struct dpram_link_device *)data; + struct pld_link_device *pld = (struct pld_link_device *)data; - dpld->udl_check.send_size += dpld->udl_param.size; - dpld->udl_check.rest_size -= dpld->udl_param.size; + pld->qc_udl_check.send_size += pld->qc_udl_param.size; + pld->qc_udl_check.rest_size -= pld->qc_udl_param.size; - dpld->udl_param.addr += dpld->udl_param.size; + pld->qc_udl_param.addr += pld->qc_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; + if (pld->qc_udl_check.send_size >= pld->qc_udl_check.total_size) { + pld->qc_udl_check.copy_complete = 1; + pld->qc_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; + if (pld->qc_udl_check.rest_size < pld->dpram->max_boot_frame_size) + pld->qc_udl_param.size = pld->qc_udl_check.rest_size; - dpld->udl_param.count += 1; + pld->qc_udl_param.count += 1; - _qc_do_download(dpld, &dpld->udl_param); + _qc_do_download(pld, &pld->qc_udl_param); } -static void qc_dload_cmd_handler(struct dpram_link_device *dpld, u16 cmd) +static void qc_dload_cmd_handler(struct pld_link_device *pld, u16 cmd) { switch (cmd) { case 0x1234: - dpld->udl_check.copy_start = 1; + pld->qc_udl_check.copy_start = 1; break; case 0xDBAB: - tasklet_schedule(&dpld->dl_tsk); + tasklet_schedule(&pld->dl_tsk); break; case 0xABCD: - mif_info("[%s] booting Start\n", dpld->ld.name); - dpld->udl_check.boot_complete = 1; + mif_info("[%s] booting Start\n", pld->ld.name); + pld->qc_udl_check.boot_complete = 1; break; default: @@ -255,22 +254,22 @@ static void qc_dload_cmd_handler(struct dpram_link_device *dpld, u16 cmd) } } -static int qc_boot_start(struct dpram_link_device *dpld) +static int qc_boot_start(struct pld_link_device *pld) { u16 mask = 0; int count = 0; /* Send interrupt -> '0x4567' */ mask = 0x4567; - dpld->send_intr(dpld, mask); + pld->send_intr(pld, mask); while (1) { - if (dpld->udl_check.boot_complete) { - dpld->udl_check.boot_complete = 0; + if (pld->qc_udl_check.boot_complete) { + pld->qc_udl_check.boot_complete = 0; break; } - msleep(20); + usleep_range(10000, 11000); count++; if (count > 200) { @@ -282,17 +281,17 @@ static int qc_boot_start(struct dpram_link_device *dpld) return 0; } -static int qc_boot_post_process(struct dpram_link_device *dpld) +static int qc_boot_post_process(struct pld_link_device *pld) { int count = 0; while (1) { - if (dpld->boot_start_complete) { - dpld->boot_start_complete = 0; + if (pld->boot_start_complete) { + pld->boot_start_complete = 0; break; } - msleep(20); + usleep_range(10000, 11000); count++; if (count > 200) { @@ -304,7 +303,7 @@ static int qc_boot_post_process(struct dpram_link_device *dpld) return 0; } -static void qc_start_handler(struct dpram_link_device *dpld) +static void qc_start_handler(struct pld_link_device *pld) { /* * INT_MASK_VALID | INT_MASK_CMD | INT_MASK_CP_AIRPLANE_BOOT | @@ -312,38 +311,38 @@ static void qc_start_handler(struct dpram_link_device *dpld) */ u16 mask = (0x0080 | 0x0040 | 0x1000 | 0x0100 | 0x0002); - dpld->boot_start_complete = 1; + pld->boot_start_complete = 1; /* Send INIT_END code to CP */ mif_info("send 0x%04X (INIT_END)\n", mask); - dpld->send_intr(dpld, mask); + pld->send_intr(pld, mask); } -static void qc_crash_log(struct dpram_link_device *dpld) +static void qc_crash_log(struct pld_link_device *pld) { - struct link_device *ld = &dpld->ld; + struct link_device *ld = &pld->ld; static unsigned char buf[151]; u8 __iomem *data = NULL; - data = dpld->get_rx_buff(dpld, IPC_FMT); + data = pld->get_rx_buff(pld, 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) +static int _qc_data_upload(struct pld_link_device *pld, + struct qc_dpram_udl_param *param) { - struct qc_dpram_boot_map *bt_map = &dpld->qc_bt_map; + struct qc_dpram_boot_map *qbt_map = &pld->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 (!gpio_get_value(pld->gpio_ipc_int2ap)) { + intval = pld->recv_intr(pld); if (intval == 0xDBAB) { break; } else { @@ -352,7 +351,7 @@ static int _qc_data_upload(struct dpram_link_device *dpld, } } - msleep(20); + usleep_range(1000, 2000); count++; if (count > 200) { @@ -361,43 +360,43 @@ static int _qc_data_upload(struct dpram_link_device *dpld, } } - iowrite16(PLD_ADDR_MASK(&bt_map->frame_size[0]), - dpld->address_buffer); - param->size = ioread16(dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&qbt_map->frame_size[0]), + pld->address_buffer); + param->size = ioread16(pld->base); - iowrite16(PLD_ADDR_MASK(&bt_map->tag[0]), - dpld->address_buffer); - param->tag = ioread16(dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&qbt_map->tag[0]), + pld->address_buffer); + param->tag = ioread16(pld->base); - iowrite16(PLD_ADDR_MASK(&bt_map->count[0]), - dpld->address_buffer); - param->count = ioread16(dpld->dp_base); + iowrite16(PLD_ADDR_MASK(&qbt_map->count[0]), + pld->address_buffer); + param->count = ioread16(pld->base); - iowrite16(PLD_ADDR_MASK(&bt_map->buff[0]), - dpld->address_buffer); - memcpy(param->addr, dpld->dp_base, param->size); + iowrite16(PLD_ADDR_MASK(&qbt_map->buff[0]), + pld->address_buffer); + memcpy(param->addr, pld->base, param->size); - dpld->send_intr(dpld, 0xDB12); + pld->send_intr(pld, 0xDB12); return retval; } -static int qc_uload_step1(struct dpram_link_device *dpld) +static int qc_uload_step1(struct pld_link_device *pld) { int retval = 0; int count = 0; u16 intval = 0; u16 mask = 0; - qc_dload_map(dpld, 1); + qc_dload_map(pld, 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); + if (!gpio_get_value(pld->gpio_ipc_int2ap)) { + intval = pld->recv_intr(pld); mif_info("intr 0x%04x\n", intval); if (intval == 0x1234) { break; @@ -407,11 +406,11 @@ static int qc_uload_step1(struct dpram_link_device *dpld) } } - msleep(20); + usleep_range(1000, 2000); count++; if (count > 200) { - intval = dpld->recv_intr(dpld); + intval = pld->recv_intr(pld); mif_info("count %d, intr 0x%04x\n", count, intval); if (intval == 0x1234) break; @@ -420,15 +419,15 @@ static int qc_uload_step1(struct dpram_link_device *dpld) } mask = 0xDEAD; - dpld->send_intr(dpld, mask); + pld->send_intr(pld, mask); return retval; } -static int qc_uload_step2(struct dpram_link_device *dpld, void *arg) +static int qc_uload_step2(struct pld_link_device *pld, void *arg) { int retval = 0; - struct dpram_udl_param param; + struct qc_dpram_udl_param param; retval = copy_from_user((void *)¶m, (void *)arg, sizeof(param)); if (retval < 0) { @@ -436,7 +435,7 @@ static int qc_uload_step2(struct dpram_link_device *dpld, void *arg) return -1; } - retval = _qc_data_upload(dpld, ¶m); + retval = _qc_data_upload(pld, ¶m); if (retval < 0) { mif_err("ERR! _qc_data_upload fail (err %d)\n", retval); return -1; @@ -446,7 +445,7 @@ static int qc_uload_step2(struct dpram_link_device *dpld, void *arg) mif_info("param->count = %d\n", param.count); if (param.tag == 4) { - enable_irq(dpld->irq); + enable_irq(pld->irq); mif_info("param->tag = %d\n", param.tag); } @@ -459,57 +458,57 @@ static int qc_uload_step2(struct dpram_link_device *dpld, void *arg) return retval; } -static int qc_ioctl(struct dpram_link_device *dpld, struct io_device *iod, +static int qc_ioctl(struct pld_link_device *pld, struct io_device *iod, unsigned int cmd, unsigned long arg) { - struct link_device *ld = &dpld->ld; + struct link_device *ld = &pld->ld; int err = 0; switch (cmd) { case IOCTL_DPRAM_PHONE_POWON: - err = qc_prepare_download(dpld); + err = qc_prepare_download(pld); 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); + err = qc_download_bin(pld, (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); + err = qc_download_nv(pld, (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); + err = qc_boot_start(pld); if (err < 0) { mif_info("%s: ERR! boot_start fail\n", ld->name); break; } - err = qc_boot_post_process(dpld); + err = qc_boot_post_process(pld); 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); + disable_irq_nosync(pld->irq); + err = qc_uload_step1(pld); if (err < 0) { - enable_irq(dpld->irq); + enable_irq(pld->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); + err = qc_uload_step2(pld, (void *)arg); if (err < 0) { - enable_irq(dpld->irq); + enable_irq(pld->irq); mif_info("%s: ERR! upload_step2 fail\n", ld->name); } break; @@ -524,7 +523,7 @@ static int qc_ioctl(struct dpram_link_device *dpld, struct io_device *iod, } #endif -static struct dpram_ext_op ext_op_set[] = { +static struct pld_ext_op ext_op_set[] = { #if defined(CONFIG_CDMA_MODEM_MDM6600) [QC_MDM6600] = { .exist = 1, @@ -547,7 +546,7 @@ static struct dpram_ext_op ext_op_set[] = { #endif }; -struct dpram_ext_op *dpram_get_ext_op(enum modem_t modem) +struct pld_ext_op *pld_get_ext_op(enum modem_t modem) { if (ext_op_set[modem].exist) return &ext_op_set[modem]; diff --git a/drivers/misc/modem_if/modem_link_device_shmem.h b/drivers/misc/modem_if/modem_link_device_shmem.h new file mode 100644 index 0000000..1f33c2a --- /dev/null +++ b/drivers/misc/modem_if/modem_link_device_shmem.h @@ -0,0 +1,700 @@ +/* + * 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_SHMEM_H__ +#define __MODEM_LINK_DEVICE_SHMEM_H__ + +#include "modem_utils.h" +#include "modem_link_device_memory.h" + +#define SHM_BOOT_MAGIC 0x424F4F54 +#define SHM_DUMP_MAGIC 0x44554D50 +#define SHM_IPC_MAGIC 0xAA +#define SHM_PM_MAGIC 0x5F + +#define SHM_4M_RESERVED_SZ 4056 +#define SHM_4M_FMT_TX_BUFF_SZ 4096 +#define SHM_4M_FMT_RX_BUFF_SZ 4096 +#define SHM_4M_RAW_TX_BUFF_SZ 2084864 +#define SHM_4M_RAW_RX_BUFF_SZ 2097152 + +struct shmem_4mb_phys_map { + u32 magic; + u32 access; + + u32 fmt_tx_head; + u32 fmt_tx_tail; + + u32 fmt_rx_head; + u32 fmt_rx_tail; + + u32 raw_tx_head; + u32 raw_tx_tail; + + u32 raw_rx_head; + u32 raw_rx_tail; + + u8 reserved[SHM_4M_RESERVED_SZ]; + + u8 fmt_tx_buff[SHM_4M_FMT_TX_BUFF_SZ]; + u8 fmt_rx_buff[SHM_4M_FMT_RX_BUFF_SZ]; + + u8 raw_tx_buff[SHM_4M_RAW_TX_BUFF_SZ]; + u8 raw_rx_buff[SHM_4M_RAW_RX_BUFF_SZ]; +} __packed; + +struct shmem_circ { + u32 __iomem *head; + u32 __iomem *tail; + u8 __iomem *buff; + u32 size; +}; + +struct shmem_ipc_device { + char name[16]; + int id; + + struct shmem_circ txq; + struct shmem_circ rxq; + + u16 mask_req_ack; + u16 mask_res_ack; + u16 mask_send; + + int req_ack_rcvd; +}; + +struct shmem_ipc_map { + u32 __iomem *magic; + u32 __iomem *access; + + struct shmem_ipc_device dev[MAX_SIPC5_DEV]; + + u32 __iomem *mbx2ap; + u32 __iomem *mbx2cp; +}; + +struct shmem_link_device { + struct link_device ld; + + enum shmem_type type; + + /* SHMEM (SHARED MEMORY) address and size */ + u32 start; /* physical "start" address of SHMEM */ + u32 size; /* size of SHMEM */ + u8 __iomem *base; /* virtual address of the "start" */ + + /* SHMEM GPIO & IRQ */ + unsigned gpio_pda_active; + + unsigned gpio_ap_wakeup; + int irq_ap_wakeup; + unsigned gpio_ap_status; + + unsigned gpio_cp_wakeup; + unsigned gpio_cp_status; + int irq_cp_status; + + /* IPC device map */ + struct shmem_ipc_map ipc_map; + + /* Pointers (aliases) to IPC device map */ + u32 __iomem *magic; + u32 __iomem *access; + struct shmem_ipc_device *dev[MAX_SIPC5_DEV]; + u32 __iomem *mbx2ap; + u32 __iomem *mbx2cp; + + /* Wakelock for SHMEM device */ + struct wake_lock wlock; + char wlock_name[MIF_MAX_NAME_LEN]; + struct wake_lock ap_wlock; + char ap_wlock_name[MIF_MAX_NAME_LEN]; + struct wake_lock cp_wlock; + char cp_wlock_name[MIF_MAX_NAME_LEN]; + + /* for UDL */ + struct completion udl_cmpl; + struct std_dload_info dl_info; + + /* for CP crash dump */ + bool forced_cp_crash; + struct timer_list crash_ack_timer; + + /* for locking TX process */ + spinlock_t tx_lock[MAX_SIPC5_DEV]; + + /* for retransmission under SHMEM flow control after TXQ full state */ + atomic_t res_required[MAX_SIPC5_DEV]; + struct completion req_ack_cmpl[MAX_SIPC5_DEV]; + + /* for efficient RX process */ + struct tasklet_struct rx_tsk; + struct delayed_work ipc_rx_dwork; + struct delayed_work udl_rx_dwork; + struct io_device *iod[MAX_SIPC5_DEV]; + + /* for logging SHMEM status */ + struct mem_status_queue stat_list; + + /* for logging SHMEM dump */ + struct trace_data_queue trace_list; +#ifdef DEBUG_MODEM_IF + struct delayed_work dump_dwork; + char dump_path[MIF_MAX_PATH_LEN]; +#endif + + /* to hold/release "cp_wakeup" for PM (power-management) */ + struct delayed_work cp_sleep_dwork; + struct delayed_work link_off_dwork; + atomic_t ref_cnt; + spinlock_t pm_lock; +}; + +/* converts from struct link_device* to struct xxx_link_device* */ +#define to_shmem_link_device(linkdev) \ + container_of(linkdev, struct shmem_link_device, ld) + +#if 1 +#endif + +/** + * get_magic + * @shmd: pointer to an instance of shmem_link_device structure + * + * Returns the value of the "magic code" field. + */ +static inline u32 get_magic(struct shmem_link_device *shmd) +{ + return ioread32(shmd->magic); +} + +/** + * get_access + * @shmd: pointer to an instance of shmem_link_device structure + * + * Returns the value of the "access enable" field. + */ +static inline u32 get_access(struct shmem_link_device *shmd) +{ + return ioread32(shmd->access); +} + +/** + * set_magic + * @shmd: pointer to an instance of shmem_link_device structure + * @val: value to be written to the "magic code" field + */ +static inline void set_magic(struct shmem_link_device *shmd, u32 val) +{ + iowrite32(val, shmd->magic); +} + +/** + * set_access + * @shmd: pointer to an instance of shmem_link_device structure + * @val: value to be written to the "access enable" field + */ +static inline void set_access(struct shmem_link_device *shmd, u32 val) +{ + iowrite32(val, shmd->access); +} + +/** + * get_txq_head + * @shmd: pointer to an instance of shmem_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Returns the value of a head (in) pointer in a TX queue. + */ +static inline u32 get_txq_head(struct shmem_link_device *shmd, int id) +{ + return ioread32(shmd->dev[id]->txq.head); +} + +/** + * get_txq_tail + * @shmd: pointer to an instance of shmem_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Returns the value of a tail (out) pointer in a TX queue. + * + * It is useless for an AP to read a tail pointer in a TX queue twice to verify + * whether or not the value in the pointer is valid, because it can already have + * been updated by a CP after the first access from the AP. + */ +static inline u32 get_txq_tail(struct shmem_link_device *shmd, int id) +{ + return ioread32(shmd->dev[id]->txq.tail); +} + +/** + * get_txq_buff + * @shmd: pointer to an instance of shmem_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Returns the start address of the buffer in a TXQ. + */ +static inline u8 *get_txq_buff(struct shmem_link_device *shmd, int id) +{ + return shmd->dev[id]->txq.buff; +} + +/** + * get_txq_buff_size + * @shmd: pointer to an instance of shmem_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Returns the size of the buffer in a TXQ. + */ +static inline u32 get_txq_buff_size(struct shmem_link_device *shmd, int id) +{ + return shmd->dev[id]->txq.size; +} + +/** + * get_rxq_head + * @shmd: pointer to an instance of shmem_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Returns the value of a head (in) pointer in an RX queue. + * + * It is useless for an AP to read a head pointer in an RX queue twice to verify + * whether or not the value in the pointer is valid, because it can already have + * been updated by a CP after the first access from the AP. + */ +static inline u32 get_rxq_head(struct shmem_link_device *shmd, int id) +{ + return ioread32(shmd->dev[id]->rxq.head); +} + +/** + * get_rxq_tail + * @shmd: pointer to an instance of shmem_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Returns the value of a tail (in) pointer in an RX queue. + */ +static inline u32 get_rxq_tail(struct shmem_link_device *shmd, int id) +{ + return ioread32(shmd->dev[id]->rxq.tail); +} + +/** + * get_rxq_buff + * @shmd: pointer to an instance of shmem_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Returns the start address of the buffer in an RXQ. + */ +static inline u8 *get_rxq_buff(struct shmem_link_device *shmd, int id) +{ + return shmd->dev[id]->rxq.buff; +} + +/** + * get_rxq_buff_size + * @shmd: pointer to an instance of shmem_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Returns the size of the buffer in an RXQ. + */ +static inline u32 get_rxq_buff_size(struct shmem_link_device *shmd, int id) +{ + return shmd->dev[id]->rxq.size; +} + +/** + * set_txq_head + * @shmd: pointer to an instance of shmem_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * @in: value to be written to the head pointer in a TXQ + */ +static inline void set_txq_head(struct shmem_link_device *shmd, int id, u32 in) +{ + iowrite32(in, shmd->dev[id]->txq.head); +} + +/** + * set_txq_tail + * @shmd: pointer to an instance of shmem_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * @out: value to be written to the tail pointer in a TXQ + */ +static inline void set_txq_tail(struct shmem_link_device *shmd, int id, u32 out) +{ + iowrite32(out, shmd->dev[id]->txq.tail); +} + +/** + * set_rxq_head + * @shmd: pointer to an instance of shmem_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * @in: value to be written to the head pointer in an RXQ + */ +static inline void set_rxq_head(struct shmem_link_device *shmd, int id, u32 in) +{ + iowrite32(in, shmd->dev[id]->rxq.head); +} + +/** + * set_rxq_tail + * @shmd: pointer to an instance of shmem_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * @out: value to be written to the tail pointer in an RXQ + */ +static inline void set_rxq_tail(struct shmem_link_device *shmd, int id, u32 out) +{ + iowrite32(out, shmd->dev[id]->rxq.tail); +} + +/** + * get_mask_req_ack + * @shmd: pointer to an instance of shmem_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Returns the REQ_ACK mask value for the IPC device. + */ +static inline u16 get_mask_req_ack(struct shmem_link_device *shmd, int id) +{ + return shmd->dev[id]->mask_req_ack; +} + +/** + * get_mask_res_ack + * @shmd: pointer to an instance of shmem_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Returns the RES_ACK mask value for the IPC device. + */ +static inline u16 get_mask_res_ack(struct shmem_link_device *shmd, int id) +{ + return shmd->dev[id]->mask_res_ack; +} + +/** + * get_mask_send + * @shmd: pointer to an instance of shmem_link_device structure + * @id: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Returns the SEND mask value for the IPC device. + */ +static inline u16 get_mask_send(struct shmem_link_device *shmd, int id) +{ + return shmd->dev[id]->mask_send; +} + +#ifndef CONFIG_LINK_DEVICE_C2C +/** + * read_int2cp + * @shmd: pointer to an instance of shmem_link_device structure + * + * Returns the value of the AP-to-CP interrupt register. + */ +static inline u16 read_int2cp(struct shmem_link_device *shmd) +{ + if (shmd->mbx2cp) + return ioread16(shmd->mbx2cp); + else + return 0; +} +#endif + +/** + * reset_txq_circ + * @shmd: pointer to an instance of shmem_link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Empties a TXQ by resetting the head (in) pointer with the value in the tail + * (out) pointer. + */ +static inline void reset_txq_circ(struct shmem_link_device *shmd, int dev) +{ + struct link_device *ld = &shmd->ld; + u32 head = get_txq_head(shmd, dev); + u32 tail = get_txq_tail(shmd, dev); + + mif_err("%s: %s_TXQ: HEAD[%u] <== TAIL[%u]\n", + ld->name, get_dev_name(dev), head, tail); + + set_txq_head(shmd, dev, tail); +} + +/** + * reset_rxq_circ + * @shmd: pointer to an instance of shmem_link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * + * Empties an RXQ by resetting the tail (out) pointer with the value in the head + * (in) pointer. + */ +static inline void reset_rxq_circ(struct shmem_link_device *shmd, int dev) +{ + struct link_device *ld = &shmd->ld; + u32 head = get_rxq_head(shmd, dev); + u32 tail = get_rxq_tail(shmd, dev); + + mif_err("%s: %s_RXQ: TAIL[%u] <== HEAD[%u]\n", + ld->name, get_dev_name(dev), tail, head); + + set_rxq_tail(shmd, dev, head); +} + +/** + * ipc_active + * @shmd: pointer to an instance of shmem_link_device structure + * + * Returns whether or not IPC via the shmem_link_device instance is possible. + */ +static bool ipc_active(struct shmem_link_device *shmd) +{ + struct link_device *ld = &shmd->ld; + u32 magic = get_magic(shmd); + u32 access = get_access(shmd); + + /* Check link mode */ + if (unlikely(ld->mode != LINK_MODE_IPC)) { + mif_err("%s: <by %pf> ERR! ld->mode != LINK_MODE_IPC\n", + ld->name, CALLER); + return false; + } + + /* Check "magic code" and "access enable" values */ + if (unlikely(magic != SHM_IPC_MAGIC || access != 1)) { + mif_err("%s: <by %pf> ERR! magic:0x%X access:%d\n", + ld->name, CALLER, magic, access); + return false; + } + + return true; +} + +/** + * get_rxq_rcvd + * @shmd: pointer to an instance of shmem_link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * @mst: pointer to an instance of mem_status structure + * OUT @circ: pointer to an instance of circ_status structure + * + * Stores {start address of the buffer in a RXQ, size of the buffer, in & out + * pointer values, size of received data} into the 'circ' instance. + * + * Returns an error code. + */ +static int get_rxq_rcvd(struct shmem_link_device *shmd, int dev, + struct mem_status *mst, struct circ_status *circ) +{ + struct link_device *ld = &shmd->ld; + + circ->buff = get_rxq_buff(shmd, dev); + circ->qsize = get_rxq_buff_size(shmd, dev); + circ->in = mst->head[dev][RX]; + circ->out = mst->tail[dev][RX]; + circ->size = circ_get_usage(circ->qsize, circ->in, circ->out); + + if (circ_valid(circ->qsize, circ->in, circ->out)) { + mif_debug("%s: %s_RXQ qsize[%u] in[%u] out[%u] rcvd[%u]\n", + ld->name, get_dev_name(dev), circ->qsize, circ->in, + circ->out, circ->size); + return 0; + } else { + mif_err("%s: ERR! %s_RXQ invalid (qsize[%d] in[%d] out[%d])\n", + ld->name, get_dev_name(dev), circ->qsize, circ->in, + circ->out); + return -EIO; + } +} + +/** + * get_txq_space + * @shmd: pointer to an instance of shmem_link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * OUT @circ: pointer to an instance of circ_status structure + * + * Stores {start address of the buffer in a TXQ, size of the buffer, in & out + * pointer values, size of free space} into the 'circ' instance. + * + * Returns the size of free space in the buffer or an error code. + */ +static int get_txq_space(struct shmem_link_device *shmd, int dev, + struct circ_status *circ) +{ + struct link_device *ld = &shmd->ld; + int cnt = 0; + u32 qsize; + u32 head; + u32 tail; + int space; + + while (1) { + qsize = get_txq_buff_size(shmd, dev); + head = get_txq_head(shmd, dev); + tail = get_txq_tail(shmd, dev); + space = circ_get_space(qsize, head, tail); + + 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 (circ_valid(qsize, head, tail)) + break; + + cnt++; + mif_err("%s: ERR! invalid %s_TXQ{qsize:%d in:%d out:%d " + "space:%d}, count %d\n", + ld->name, get_dev_name(dev), qsize, head, tail, + space, cnt); + if (cnt >= MAX_RETRY_CNT) { + space = -EIO; + break; + } + + udelay(100); + } + + circ->buff = get_txq_buff(shmd, dev); + circ->qsize = qsize; + circ->in = head; + circ->out = tail; + circ->size = space; + + return space; +} + +/** + * get_txq_saved + * @shmd: pointer to an instance of shmem_link_device structure + * @dev: IPC device (IPC_FMT, IPC_RAW, etc.) + * @mst: pointer to an instance of mem_status structure + * OUT @circ: pointer to an instance of circ_status structure + * + * Stores {start address of the buffer in a TXQ, size of the buffer, in & out + * pointer values, size of stored data} into the 'circ' instance. + * + * Returns an error code. + */ +static int get_txq_saved(struct shmem_link_device *shmd, int dev, + struct circ_status *circ) +{ + struct link_device *ld = &shmd->ld; + int cnt = 0; + u32 qsize; + u32 head; + u32 tail; + int saved; + + while (1) { + qsize = get_txq_buff_size(shmd, dev); + head = get_txq_head(shmd, dev); + tail = get_txq_tail(shmd, dev); + saved = circ_get_usage(qsize, head, tail); + + mif_debug("%s: %s_TXQ{qsize:%u in:%u out:%u saved:%u}\n", + ld->name, get_dev_name(dev), qsize, head, tail, saved); + + if (circ_valid(qsize, head, tail)) + break; + + cnt++; + mif_err("%s: ERR! invalid %s_TXQ{qsize:%d in:%d out:%d " + "saved:%d}, count %d\n", + ld->name, get_dev_name(dev), qsize, head, tail, + saved, cnt); + if (cnt >= MAX_RETRY_CNT) { + saved = -EIO; + break; + } + + udelay(100); + } + + circ->buff = get_txq_buff(shmd, dev); + circ->qsize = qsize; + circ->in = head; + circ->out = tail; + circ->size = saved; + + return saved; +} + +/** + * clear_shmem_map + * @shmd: pointer to an instance of shmem_link_device structure + * + * Clears all pointers in every circular queue. + */ +static void clear_shmem_map(struct shmem_link_device *shmd) +{ + set_txq_head(shmd, IPC_FMT, 0); + set_txq_tail(shmd, IPC_FMT, 0); + set_rxq_head(shmd, IPC_FMT, 0); + set_rxq_tail(shmd, IPC_FMT, 0); + + set_txq_head(shmd, IPC_RAW, 0); + set_txq_tail(shmd, IPC_RAW, 0); + set_rxq_head(shmd, IPC_RAW, 0); + set_rxq_tail(shmd, IPC_RAW, 0); +} + +/** + * reset_shmem_ipc + * @shmd: pointer to an instance of shmem_link_device structure + * + * Reset SHMEM with IPC map. + */ +static void reset_shmem_ipc(struct shmem_link_device *shmd) +{ + set_access(shmd, 0); + + clear_shmem_map(shmd); + + atomic_set(&shmd->res_required[IPC_FMT], 0); + atomic_set(&shmd->res_required[IPC_RAW], 0); + + atomic_set(&shmd->ref_cnt, 0); + + set_magic(shmd, SHM_IPC_MAGIC); + set_access(shmd, 1); +} + +/** + * init_shmem_ipc + * @shmd: pointer to an instance of shmem_link_device structure + * + * Initializes IPC via SHMEM. + */ +static int init_shmem_ipc(struct shmem_link_device *shmd) +{ + struct link_device *ld = &shmd->ld; + + if (ld->mode == LINK_MODE_IPC && + get_magic(shmd) == SHM_IPC_MAGIC && + get_access(shmd) == 1) { + mif_err("%s: IPC already initialized\n", ld->name); + return 0; + } + + /* Initialize variables for efficient TX/RX processing */ + shmd->iod[IPC_FMT] = link_get_iod_with_format(ld, IPC_FMT); + shmd->iod[IPC_RAW] = link_get_iod_with_format(ld, IPC_MULTI_RAW); + + reset_shmem_ipc(shmd); + + if (get_magic(shmd) != SHM_IPC_MAGIC || get_access(shmd) != 1) + return -EACCES; + + return 0; +} + +#endif + diff --git a/drivers/misc/modem_if/modem_link_device_spi.c b/drivers/misc/modem_if/modem_link_device_spi.c index c4715e0..ece6b65 100644 --- a/drivers/misc/modem_if/modem_link_device_spi.c +++ b/drivers/misc/modem_if/modem_link_device_spi.c @@ -26,9 +26,10 @@ #include <linux/if_arp.h> #include <linux/platform_device.h> #include <linux/spi/spi.h> +#include <linux/suspend.h> #include <linux/kthread.h> -#include <linux/platform_data/modem.h> +#include "modem.h" #include "modem_prj.h" #include "modem_link_device_spi.h" #include "modem_utils.h" @@ -66,8 +67,7 @@ static irqreturn_t spi_srdy_irq_handler(int irq, void *p_ld) if (!spild->boot_done) return result; - if (!wake_lock_active(&spild->spi_wake_lock) - && spild->send_modem_spi != 1) { + if (!wake_lock_active(&spild->spi_wake_lock)) { 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); @@ -311,7 +311,7 @@ static void spi_prepare_tx_packet(void) ld = &p_spild->ld; - for (i = 0; i < p_spild->max_ipc_dev; i++) { + for (i = 0; i < ld->max_ipc_dev; i++) { while ((skb = skb_dequeue(ld->skb_txq[i]))) { ret = spi_buff_write(p_spild, i, skb->data, skb->len); if (!ret) { @@ -331,7 +331,7 @@ static void spi_start_data_send(void) ld = &p_spild->ld; - for (i = 0; i < p_spild->max_ipc_dev; i++) { + for (i = 0; i < ld->max_ipc_dev; i++) { if (skb_queue_len(ld->skb_txq[i]) > 0) spi_send_work(SPI_WORK_SEND, SPI_WORK); } @@ -345,6 +345,14 @@ static void spi_tx_work(void) char *spi_sync_buf; spild = p_spild; + iod = link_get_iod_with_format(&spild->ld, IPC_FMT); + if (!iod) { + mif_err("no iodevice for modem control\n"); + return; + } + + if (iod->mc->phone_state == STATE_CRASH_EXIT) + return; /* check SUB SRDY, SRDY state */ if (gpio_get_value(spild->gpio_ipc_sub_srdy) == @@ -420,8 +428,6 @@ static void spi_tx_work(void) 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); @@ -553,6 +559,15 @@ static void spi_rx_work(void) if (!spild) pr_err("[LNK/E] <%s> dpld == NULL\n", __func__); + iod = link_get_iod_with_format(&spild->ld, IPC_FMT); + if (!iod) { + mif_err("no iodevice for modem control\n"); + return; + } + + if (iod->mc->phone_state == STATE_CRASH_EXIT) + return; + if (!wake_lock_active(&spild->spi_wake_lock) || gpio_get_value(spild->gpio_ipc_srdy) == SPI_GPIOLEVEL_LOW || get_console_suspended() || @@ -606,7 +621,7 @@ static void spi_rx_work(void) /* 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++) { + for (i = 0; i < ld->max_ipc_dev; i++) { iod = spild->iod[i]; while ((skb = skb_dequeue(&spild->skb_rxq[i])) != NULL) { @@ -623,8 +638,6 @@ static void spi_rx_work(void) "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); @@ -639,6 +652,34 @@ static void spi_rx_work(void) spi_start_data_send(); } + +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; +} + unsigned int sprd_crc_calc(char *buf_ptr, unsigned int len) { unsigned int i; @@ -1239,10 +1280,19 @@ err3: static void spi_send_modem_bin(struct work_struct *send_modem_w) { + struct spi_link_device *spild; + struct io_device *iod; int retval; struct image_buf img; unsigned long tick1, tick2 = 0; + spild = p_spild; + iod = link_get_iod_with_format(&spild->ld, IPC_FMT); + if (!iod) { + mif_err("no iodevice for modem control\n"); + return; + } + tick1 = jiffies_to_msecs(jiffies); retval = spi_send_modem_bin_xmit_img(MODEM_MAIN, &img); @@ -1285,16 +1335,25 @@ static void spi_send_modem_bin(struct work_struct *send_modem_w) tick2 = jiffies_to_msecs(jiffies); pr_info("Downloading takes %lu msec\n", (tick2-tick1)); - complete_all(&p_spild->ril_init); + spi_init_ipc(p_spild); + sprd_boot_done = 1; p_spild->ril_send_cnt = 0; + p_spild->spi_state = SPI_STATE_IDLE; + if (iod) + iod->modem_state_changed(iod, + STATE_ONLINE); + return; err: + if (iod) + iod->modem_state_changed(iod, + STATE_OFFLINE); return; } -static inline int _request_mem(struct ipc_spi *od) +static inline int _request_mem(struct spi_v_buff *od) { if (!p_spild->p_virtual_buff) { od->mmio = vmalloc(od->size); @@ -1320,7 +1379,7 @@ 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"); + pr_debug("[SPI] spi_tx_timer_callback -timer expires\n"); } } @@ -1328,7 +1387,7 @@ 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"); + pr_debug("[SPI] spi_rx_timer_callback -timer expires\n"); } } @@ -1381,96 +1440,37 @@ static void spi_work(struct work_struct *work) } } -static int spi_init_ipc(struct spi_link_device *spild) +static int link_pm_notifier_event(struct notifier_block *this, + unsigned long event, void *ptr) { - 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; + struct io_device *iod; + struct link_pm_data *pm_data = + container_of(this, struct link_pm_data, pm_notifier); - if (lpcharge == 1) { - pr_err("[LPM MODE] spi_thread_exit!\n"); - return 0; + iod = link_get_iod_with_format(&pm_data->spild->ld, IPC_FMT); + if (!iod) { + pr_err("no iodevice for modem control\n"); + return NOTIFY_BAD; } - 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__); + if (!gpio_get_value(iod->mc->gpio_phone_active)) + return NOTIFY_DONE; - pr_info("<%s> wait 2 sec... srdy : %d\n", - __func__, gpio_get_value(spild->gpio_ipc_srdy)); - msleep_interruptible(1700); + switch (event) { + case PM_SUSPEND_PREPARE: + /* set TD PDA Active High if previous state was LPA */ + mif_info("TD PDA active low to LPA suspend spot\n"); + gpio_set_value(iod->mc->gpio_pda_active, 0); - 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"); + return NOTIFY_OK; + case PM_POST_SUSPEND: + /* LPA to Kernel suspend and User Freezing task fail resume, + restore to LPA GPIO states. */ + mif_info("TD PDA active High to LPA GPIO state\n"); + gpio_set_value(iod->mc->gpio_pda_active, 1); + return NOTIFY_OK; } - /* 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; + return NOTIFY_DONE; } static int spi_probe(struct spi_device *spi) @@ -1512,8 +1512,7 @@ static struct spi_driver spi_driver = { static int spi_link_init(void) { int ret; - struct ipc_spi *od; - struct task_struct *th; + struct spi_v_buff *od; struct link_device *ld = &p_spild->ld; p_spild->gpio_modem_bin_srdy = p_spild->gpio_ipc_srdy; @@ -1522,7 +1521,7 @@ static int spi_link_init(void) 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); + od = kzalloc(sizeof(struct spi_v_buff), GFP_KERNEL); if (!od) { pr_err("(%d) failed to allocate device\n", __LINE__); ret = -ENOMEM; @@ -1537,7 +1536,6 @@ static int spi_link_init(void) if (ret) goto err; - init_completion(&p_spild->ril_init); sema_init(&p_spild->srdy_sem, 0); INIT_WORK(&p_spild->send_modem_w, @@ -1563,12 +1561,7 @@ static int spi_link_init(void) 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); + p_spild->boot_done = 1; pr_info("[%s] Done\n", __func__); return 0; @@ -1591,7 +1584,6 @@ void spi_set_restart(void) 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); @@ -1635,6 +1627,35 @@ exit: } EXPORT_SYMBOL(spi_thread_restart); +static int spi_link_pm_init(struct spi_link_device *spild, + struct platform_device *pdev) +{ + struct modem_data *pdata = + (struct modem_data *)pdev->dev.platform_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; + } + + pm_data->spild = spild; + spild->link_pm_data = pm_data; + + pm_data->pm_notifier.notifier_call = link_pm_notifier_event; + register_pm_notifier(&pm_data->pm_notifier); + + return 0; +} + struct link_device *spi_create_link_device(struct platform_device *pdev) { struct spi_link_device *spild = NULL; @@ -1689,9 +1710,9 @@ struct link_device *spi_create_link_device(struct platform_device *pdev) } spild->spi_state = SPI_STATE_END; - spild->max_ipc_dev = IPC_RFS+1; /* FMT, RAW, RFS */ + ld->max_ipc_dev = (IPC_RFS + 1); /* FMT, RAW, RFS */ - for (i = 0; i < spild->max_ipc_dev; i++) + for (i = 0; i < ld->max_ipc_dev; i++) skb_queue_head_init(&spild->skb_rxq[i]); /* Prepare a clean buffer for SPI access */ @@ -1738,6 +1759,11 @@ struct link_device *spi_create_link_device(struct platform_device *pdev) goto err; } + /* create link pm device */ + ret = spi_link_pm_init(spild, pdev); + if (ret) + goto err; + /* Create SPI device */ ret = spi_link_init(); if (ret) diff --git a/drivers/misc/modem_if/modem_link_device_spi.h b/drivers/misc/modem_if/modem_link_device_spi.h index 210d815..b1b334f 100644 --- a/drivers/misc/modem_if/modem_link_device_spi.h +++ b/drivers/misc/modem_if/modem_link_device_spi.h @@ -18,7 +18,7 @@ #include <linux/wakelock.h> #include <linux/workqueue.h> #include <linux/timer.h> -#include <linux/platform_data/modem.h> +#include "modem.h" #define SPI_TIMER_TX_WAIT_TIME 60 /* ms */ @@ -135,20 +135,24 @@ struct spi_data_packet_header { unsigned long more:1; }; +struct link_pm_data { + struct miscdevice miscdev; + struct spi_link_device *spild; + + struct notifier_block pm_notifier; +}; + 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; @@ -182,11 +186,13 @@ struct spi_link_device { struct io_device *iod[MAX_DEV_FORMAT]; struct sk_buff_head skb_rxq[MAX_DEV_FORMAT]; + /* LINK PM DEVICE DATA */ + struct link_pm_data *link_pm_data; + /* Multi-purpose miscellaneous buffer */ u8 *buff; u8 *sync_buff; - struct completion ril_init; struct semaphore srdy_sem; int send_modem_spi; @@ -202,6 +208,12 @@ struct spi_link_device { #define to_spi_link_device(linkdev) \ container_of(linkdev, struct spi_link_device, ld) +struct spi_v_buff { + unsigned long base; + unsigned long size; + void __iomem *mmio; +}; + extern unsigned int lpcharge; extern int get_console_suspended(void); static void spi_work(struct work_struct *work); diff --git a/drivers/misc/modem_if/modem_link_device_usb.c b/drivers/misc/modem_if/modem_link_device_usb.c index 14aee9f..9e6cbea 100644 --- a/drivers/misc/modem_if/modem_link_device_usb.c +++ b/drivers/misc/modem_if/modem_link_device_usb.c @@ -13,7 +13,7 @@ * */ -/* #define DEBUG */ +#define DEBUG #include <linux/init.h> #include <linux/module.h> @@ -26,7 +26,7 @@ #include <linux/platform_device.h> #include <linux/suspend.h> -#include <linux/platform_data/modem.h> +#include "modem.h" #include "modem_prj.h" #include "modem_link_device_usb.h" #include "modem_utils.h" @@ -297,10 +297,15 @@ static void usb_change_modem_state(struct usb_link_device *usb_ld, enum modem_state state) { struct io_device *iod; + struct io_device *bootd; iod = link_get_iod_with_format(&usb_ld->ld, IPC_FMT); if (iod) iod->modem_state_changed(iod, state); + + bootd = usb_ld->ld.mc->bootd; + if (bootd) + bootd->modem_state_changed(bootd, state); } static int usb_tx_urb_with_skb(struct usb_link_device *usb_ld, @@ -624,7 +629,12 @@ 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); + if (!has_hub(usb_ld)) { + if (pm_data->root_hub) + pm_runtime_forbid(pm_data->root_hub); + schedule_delayed_work(&usb_ld->wait_enumeration, + WAIT_ENUMURATION_TIMEOUT_JIFFIES); + } } } diff --git a/drivers/misc/modem_if/modem_link_pm_usb.c b/drivers/misc/modem_if/modem_link_pm_usb.c index 75ad970..c63f08e 100644 --- a/drivers/misc/modem_if/modem_link_pm_usb.c +++ b/drivers/misc/modem_if/modem_link_pm_usb.c @@ -12,7 +12,7 @@ * */ -/* #define DEBUG */ +#define DEBUG #include <linux/init.h> #include <linux/module.h> @@ -27,12 +27,15 @@ #include "modem_link_pm_usb.h" +int during_hub_resume; + 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"); + during_hub_resume = 1; } schedule_delayed_work(&pm_data->link_pm_hub, msecs_to_jiffies(delay)); @@ -81,12 +84,13 @@ void link_pm_preactive(struct link_pm_data *pm_data) static void link_pm_hub_work(struct work_struct *work) { - int err; + int err, cnt; 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) { end_hub_work(pm_data); + during_hub_resume = 0; return; } @@ -111,7 +115,16 @@ static void link_pm_hub_work(struct work_struct *work) /* skip 1st time before first probe */ if (pm_data->root_hub) pm_runtime_get_sync(pm_data->root_hub); - err = pm_data->port_enable(2, 1); + + for (cnt=0;cnt<5;cnt++) { + err = pm_data->port_enable(2, 1); + if (err >= 0) { + mif_err("hub on success\n"); + break; + } + mif_err("hub on fail %d th\n", cnt); + msleep(100); + } if (err < 0) { mif_err("hub on fail err=%d\n", err); err = pm_data->port_enable(2, 0); @@ -132,6 +145,8 @@ 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); + + mif_err("USB Hub resume fail !!!\n"); end_hub_work(pm_data); } else { mif_info("hub resumming: %d\n", @@ -164,9 +179,6 @@ static int link_pm_hub_standby(void *args) /* 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; } @@ -210,6 +222,7 @@ static long link_pm_ioctl(struct file *file, unsigned int cmd, sizeof(int))) return -EFAULT; gpio_set_value(pm_data->gpio_link_active, value); + mif_info("> H-ACT %d\n", value); break; case IOCTL_LINK_GET_HOSTWAKE: return !gpio_get_value(pm_data->gpio_link_hostwake); @@ -233,7 +246,7 @@ static long link_pm_ioctl(struct file *file, unsigned int cmd, case IOCTL_LINK_PORT_OFF: err = link_pm_hub_standby(pm_data); if (err < 0) { - mif_err("usb3503 active fail\n"); + mif_err("usb3503 standby fail\n"); goto exit; } pm_data->hub_init_lock = 1; @@ -363,6 +376,8 @@ int link_pm_init(struct usb_link_device *usb_ld, void *data) pm_data->miscdev.name = "link_pm"; pm_data->miscdev.fops = &link_pm_fops; + during_hub_resume = 0; + err = misc_register(&pm_data->miscdev); if (err < 0) { mif_err("fail to register pm device(%d)\n", err); diff --git a/drivers/misc/modem_if/modem_link_pm_usb.h b/drivers/misc/modem_if/modem_link_pm_usb.h index d26af76..5fc762d 100644 --- a/drivers/misc/modem_if/modem_link_pm_usb.h +++ b/drivers/misc/modem_if/modem_link_pm_usb.h @@ -15,7 +15,7 @@ #ifndef __MODEM_LINK_PM_USB_H__ #define __MODEM_LINK_PM_USB_H__ -#include <linux/platform_data/modem.h> +#include "modem.h" #include "modem_prj.h" #include "modem_link_device_usb.h" diff --git a/drivers/misc/modem_if/modem_modemctl_device_cbp71.c b/drivers/misc/modem_if/modem_modemctl_device_cbp71.c index 28f2ce7..5a4db56 100644 --- a/drivers/misc/modem_if/modem_modemctl_device_cbp71.c +++ b/drivers/misc/modem_if/modem_modemctl_device_cbp71.c @@ -23,7 +23,7 @@ #include <linux/wait.h> #include <linux/sched.h> -#include <linux/platform_data/modem.h> +#include "modem.h" #include "modem_prj.h" #include "modem_link_device_dpram.h" diff --git a/drivers/misc/modem_if/modem_modemctl_device_cbp72.c b/drivers/misc/modem_if/modem_modemctl_device_cbp72.c index 2617be8..d8f2af9 100644 --- a/drivers/misc/modem_if/modem_modemctl_device_cbp72.c +++ b/drivers/misc/modem_if/modem_modemctl_device_cbp72.c @@ -1,6 +1,4 @@ -/* /linux/drivers/misc/modem_if/modem_modemctl_device_cbp7.1.c - * - * Copyright (C) 2010 Google, Inc. +/* * Copyright (C) 2010 Samsung Electronics. * * This software is licensed under the terms of the GNU General Public @@ -24,14 +22,13 @@ #include <linux/sched.h> #include <linux/platform_device.h> -#include <linux/platform_data/modem.h> +#include "modem.h" #include "modem_prj.h" #include "modem_link_device_dpram.h" #define PIF_TIMEOUT (180 * HZ) #define DPRAM_INIT_TIMEOUT (30 * HZ) - static irqreturn_t phone_active_handler(int irq, void *arg) { struct modem_ctl *mc = (struct modem_ctl *)arg; @@ -163,13 +160,13 @@ static int cbp72_boot_off(struct modem_ctl *mc) { int ret; struct link_device *ld = get_current_link(mc->bootd); - struct dpram_link_device *dpld = to_dpram_link_device(ld); + mif_debug("\n"); /* Wait here until the PHONE is up. * Waiting as the this called from IOCTL->UM thread */ mif_info("Waiting for INT_CMD_PHONE_START\n"); - ret = wait_for_completion_interruptible_timeout( - &dpld->dpram_init_cmd, DPRAM_INIT_TIMEOUT); + ret = wait_for_completion_interruptible_timeout(&ld->init_cmpl, + DPRAM_INIT_TIMEOUT); if (!ret) { /* ret == 0 on timeout, ret < 0 if interrupted */ mif_err("Timeout!!! (PHONE_START was not arrived.)\n"); @@ -177,8 +174,8 @@ static int cbp72_boot_off(struct modem_ctl *mc) } mif_info("Waiting for INT_CMD_PIF_INIT_DONE\n"); - ret = wait_for_completion_interruptible_timeout( - &dpld->modem_pif_init_done, PIF_TIMEOUT); + ret = wait_for_completion_interruptible_timeout(&ld->pif_cmpl, + PIF_TIMEOUT); if (!ret) { mif_err("Timeout!!! (PIF_INIT_DONE was not arrived.)\n"); return -ENXIO; @@ -199,10 +196,6 @@ static int cbp72_force_crash_exit(struct modem_ctl *mc) /* Make DUMP start */ ld->force_dump(ld, mc->bootd); - msleep_interruptible(1000); - - mc->bootd->modem_state_changed(mc->bootd, STATE_CRASH_EXIT); - return 0; } @@ -221,16 +214,15 @@ int cbp72_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata) int ret = 0; int irq = 0; unsigned long flag = 0; - struct platform_device *pdev = NULL; - - mc->gpio_cp_on = pdata->gpio_cp_on; - mc->gpio_cp_off = pdata->gpio_cp_off; - 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_on = pdata->gpio_cp_on; + mc->gpio_cp_off = pdata->gpio_cp_off; + 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; if (!mc->gpio_cp_on || !mc->gpio_cp_reset || !mc->gpio_phone_active) { @@ -245,10 +237,9 @@ int cbp72_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata) cbp72_get_ops(mc); - pdev = to_platform_device(mc->dev); - mc->irq_phone_active = platform_get_irq_byname(pdev, "cp_active_irq"); + mc->irq_phone_active = pdata->irq_phone_active; if (!mc->irq_phone_active) { - mif_err("get irq fail\n"); + mif_err("get irq_phone_active fail\n"); return -1; } diff --git a/drivers/misc/modem_if/modem_modemctl_device_cbp82.c b/drivers/misc/modem_if/modem_modemctl_device_cbp82.c new file mode 100644 index 0000000..dc5799e --- /dev/null +++ b/drivers/misc/modem_if/modem_modemctl_device_cbp82.c @@ -0,0 +1,288 @@ +/* + * 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 "modem.h" +#include "modem_prj.h" +#include "modem_utils.h" + +#define DPRAM_INIT_TIMEOUT (30 * HZ) +#define PIF_TIMEOUT (180 * HZ) + +static irqreturn_t phone_active_handler(int irq, void *arg) +{ + struct modem_ctl *mc = (struct modem_ctl *)arg; + 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); + int old_state = mc->phone_state; + int new_state = mc->phone_state; + + mif_info("old_state:%s cp_on:%d cp_reset:%d cp_active:%d\n", + get_cp_state_str(old_state), cp_on, cp_reset, cp_active); + + if (cp_reset && cp_active) { + if (mc->phone_state == STATE_BOOTING) { + new_state = STATE_ONLINE; + mc->bootd->modem_state_changed(mc->bootd, new_state); + } + } else if (cp_reset && !cp_active) { + if (mc->phone_state == STATE_ONLINE) { + new_state = STATE_CRASH_EXIT; + mc->bootd->modem_state_changed(mc->bootd, new_state); + } + } else { + new_state = STATE_OFFLINE; + if (mc->bootd && mc->bootd->modem_state_changed) + mc->bootd->modem_state_changed(mc->bootd, new_state); + } + + if (new_state != old_state) { + mif_err("%s: phone_state changed (%s -> %s\n)", + mc->name, get_cp_state_str(old_state), + get_cp_state_str(new_state)); + } + + return IRQ_HANDLED; +} + +static int cbp82_on(struct modem_ctl *mc) +{ + int cp_on = gpio_get_value(mc->gpio_cp_on); + int cp_off = gpio_get_value(mc->gpio_cp_off); + int cp_reset = gpio_get_value(mc->gpio_cp_reset); + int cp_active = gpio_get_value(mc->gpio_phone_active); + mif_err("+++\n"); + + mif_err("phone_state:%s cp_on:%d cp_off:%d cp_reset:%d cp_active:%d\n", + get_cp_state_str(mc->phone_state), cp_on, cp_off, cp_reset, + cp_active); + + /* 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); + gpio_set_value(mc->gpio_cp_off, 1); + gpio_set_value(mc->gpio_cp_reset, 0); + + msleep(500); + + cp_on = gpio_get_value(mc->gpio_cp_on); + cp_off = gpio_get_value(mc->gpio_cp_off); + cp_reset = gpio_get_value(mc->gpio_cp_reset); + cp_active = gpio_get_value(mc->gpio_phone_active); + mif_err("phone_state:%s cp_on:%d cp_off:%d cp_reset:%d cp_active:%d\n", + get_cp_state_str(mc->phone_state), cp_on, cp_off, cp_reset, + cp_active); + + gpio_set_value(mc->gpio_cp_off, 0); + gpio_set_value(mc->gpio_cp_on, 1); + + msleep(100); + + gpio_set_value(mc->gpio_cp_reset, 1); + + msleep(300); + + cp_on = gpio_get_value(mc->gpio_cp_on); + cp_off = gpio_get_value(mc->gpio_cp_off); + cp_reset = gpio_get_value(mc->gpio_cp_reset); + cp_active = gpio_get_value(mc->gpio_phone_active); + mif_err("phone_state:%s cp_on:%d cp_off:%d cp_reset:%d cp_active:%d\n", + get_cp_state_str(mc->phone_state), cp_on, cp_off, cp_reset, + cp_active); + + if (mc->gpio_pda_active) + gpio_set_value(mc->gpio_pda_active, 1); + + if (mc->bootd) + mc->bootd->modem_state_changed(mc->bootd, STATE_BOOTING); + else + mif_err("no mc->bootd\n"); + + mif_err("---\n"); + return 0; +} + +static int cbp82_off(struct modem_ctl *mc) +{ + struct link_device *ld = get_current_link(mc->bootd); + mif_err("+++\n"); + + gpio_set_value(mc->gpio_cp_reset, 0); + gpio_set_value(mc->gpio_cp_on, 0); + gpio_set_value(mc->gpio_cp_off, 1); + + mc->bootd->modem_state_changed(mc->bootd, STATE_OFFLINE); + ld->mode = LINK_MODE_OFFLINE; + + mif_err("---\n"); + return 0; +} + +static int cbp82_reset(struct modem_ctl *mc) +{ + int ret = 0; + + mif_debug("cbp82_reset()\n"); + + ret = cbp82_off(mc); + if (ret) + return -ENXIO; + + msleep(100); + + ret = cbp82_on(mc); + if (ret) + return -ENXIO; + + return 0; +} + +static int cbp82_boot_on(struct modem_ctl *mc) +{ + struct link_device *ld = get_current_link(mc->bootd); + mif_info("+++\n"); + + ld->mode = LINK_MODE_BOOT; + + mif_info("---\n"); + return 0; +} + +static int cbp82_boot_off(struct modem_ctl *mc) +{ + struct link_device *ld = get_current_link(mc->bootd); + int ret; + mif_err("+++\n"); + + /* Wait here until the PHONE is up. + * Waiting as the this called from IOCTL->UM thread */ + mif_err("Waiting for PHONE_START\n"); + ret = wait_for_completion_timeout(&ld->init_cmpl, DPRAM_INIT_TIMEOUT); + if (!ret) { + /* ret == 0 on timeout */ + mif_err("T-I-M-E-O-U-T (PHONE_START)\n"); + cbp82_off(mc); + ret = -EIO; + goto exit; + } + mif_err("recv PHONE_START\n"); + + mif_err("Waiting for PIF_INIT_DONE\n"); + ret = wait_for_completion_timeout(&ld->pif_cmpl, PIF_TIMEOUT); + if (!ret) { + /* ret == 0 on timeout */ + mif_err("T-I-M-E-O-U-T (PIF_INIT_DONE)!!!\n"); + cbp82_off(mc); + ret = -EIO; + goto exit; + } + mif_err("recv PIF_INIT_DONE\n"); + + mc->bootd->modem_state_changed(mc->bootd, STATE_ONLINE); + ret = 0; + +exit: + wake_unlock(&mc->mc_wake_lock); + mif_err("---\n"); + return ret; +} + +static int cbp82_force_crash_exit(struct modem_ctl *mc) +{ + struct link_device *ld = get_current_link(mc->bootd); + + mif_err("device = %s\n", mc->bootd->name); + + /* Make DUMP start */ + ld->force_dump(ld, mc->bootd); + + return 0; +} + +static void cbp82_get_ops(struct modem_ctl *mc) +{ + mc->ops.modem_on = cbp82_on; + mc->ops.modem_off = cbp82_off; + mc->ops.modem_reset = cbp82_reset; + mc->ops.modem_boot_on = cbp82_boot_on; + mc->ops.modem_boot_off = cbp82_boot_off; + mc->ops.modem_force_crash_exit = cbp82_force_crash_exit; +} + +int cbp82_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata) +{ + int ret = 0; + int irq = 0; + unsigned long flag = 0; + mif_err("+++\n"); + + mc->gpio_cp_on = pdata->gpio_cp_on; + mc->gpio_cp_off = pdata->gpio_cp_off; + mc->gpio_cp_reset = pdata->gpio_cp_reset; + mc->gpio_phone_active = pdata->gpio_phone_active; + + if (!mc->gpio_cp_on || !mc->gpio_cp_off || !mc->gpio_cp_reset + || !mc->gpio_phone_active) { + mif_err("no GPIO data\n"); + mif_err("---\n"); + return -ENXIO; + } + + mc->gpio_pda_active = pdata->gpio_pda_active; + + gpio_set_value(mc->gpio_cp_reset, 0); + gpio_set_value(mc->gpio_cp_off, 1); + gpio_set_value(mc->gpio_cp_on, 0); + + cbp82_get_ops(mc); + + wake_lock_init(&mc->mc_wake_lock, WAKE_LOCK_SUSPEND, "cbp82_wake_lock"); + + mc->irq_phone_active = pdata->irq_phone_active; + if (!mc->irq_phone_active) { + mif_err("get irq fail\n"); + mif_err("---\n"); + return -1; + } + mif_info("PHONE_ACTIVE IRQ# = %d\n", mc->irq_phone_active); + + irq = mc->irq_phone_active; + flag = IRQF_NO_SUSPEND | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING; + ret = request_irq(irq, phone_active_handler, flag, "cdma_active", mc); + if (ret) { + mif_err("request_irq fail (%d)\n", ret); + mif_err("---\n"); + return ret; + } + + ret = enable_irq_wake(irq); + if (ret) + mif_err("enable_irq_wake fail (%d)\n", ret); + + mif_err("---\n"); + return 0; +} + diff --git a/drivers/misc/modem_if/modem_modemctl_device_cmc221.c b/drivers/misc/modem_if/modem_modemctl_device_cmc221.c index fe8ce69..a960edb 100644 --- a/drivers/misc/modem_if/modem_modemctl_device_cmc221.c +++ b/drivers/misc/modem_if/modem_modemctl_device_cmc221.c @@ -1,6 +1,4 @@ -/* /linux/drivers/misc/modem_if/modem_modemctl_device_cmc221.c - * - * Copyright (C) 2010 Google, Inc. +/* * Copyright (C) 2010 Samsung Electronics. * * This software is licensed under the terms of the GNU General Public @@ -13,15 +11,15 @@ * GNU General Public License for more details. * */ -#include <linux/init.h> +#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 "modem.h" -#include <linux/platform_data/modem.h> #include "modem_prj.h" #include "modem_link_device_usb.h" #include "modem_link_device_dpram.h" @@ -77,7 +75,7 @@ static irqreturn_t phone_active_handler(int irq, void *arg) 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); + int txpath = gpio_get_value(mc->gpio_link_switch); bool enumerated = usb_is_enumerated(mc->msd); mif_err("txpath=%d, enumeration=%d\n", txpath, enumerated); @@ -94,6 +92,52 @@ static irqreturn_t dynamic_switching_handler(int irq, void *arg) return IRQ_HANDLED; } +#ifdef CONFIG_EXYNOS4_CPUFREQ /* Set cpu clock to 800MHz for high TP */ +static void cmc221_cpufreq_lock(struct work_struct *work) +{ + struct modem_ctl *mc; + + mc = container_of(work, struct modem_ctl, work_cpu_lock.work); + if (mc->mdm_data->link_pm_data->freq_lock) { + mif_debug("Call freq lock func.\n"); + mc->mdm_data->link_pm_data->freq_lock(mc->dev); + + cancel_delayed_work(&mc->work_cpu_unlock); + schedule_delayed_work(&mc->work_cpu_unlock, + msecs_to_jiffies(5000)); + } +} + +static void cmc221_cpufreq_unlock(struct work_struct *work) +{ + struct modem_ctl *mc; + int tp_level; + + mc = container_of(work, struct modem_ctl, work_cpu_unlock.work); + tp_level = gpio_get_value(mc->gpio_cpufreq_lock); + + mif_debug("TP Level is (%d)\n", tp_level); + if (tp_level) { + mif_debug("maintain cpufreq lock !!!\n"); + schedule_delayed_work(&mc->work_cpu_unlock, + msecs_to_jiffies(5000)); + } else { + if (mc->mdm_data->link_pm_data->freq_unlock) { + mif_debug("Call freq unlock func.\n"); + mc->mdm_data->link_pm_data->freq_unlock(mc->dev); + } + } +} + +static irqreturn_t cpufreq_lock_handler(int irq, void *arg) +{ + struct modem_ctl *mc = (struct modem_ctl *)arg; + + schedule_delayed_work(&mc->work_cpu_lock, 0); + return IRQ_HANDLED; +} +#endif + static int cmc221_on(struct modem_ctl *mc) { struct link_device *ld = get_current_link(mc->iod); @@ -204,11 +248,10 @@ static int cmc221_boot_off(struct modem_ctl *mc) { int ret; struct link_device *ld = get_current_link(mc->bootd); - struct dpram_link_device *dpld = to_dpram_link_device(ld); mif_err("%s\n", mc->name); - ret = wait_for_completion_interruptible_timeout(&dpld->dpram_init_cmd, + ret = wait_for_completion_interruptible_timeout(&ld->init_cmpl, DPRAM_INIT_TIMEOUT); if (!ret) { /* ret == 0 on timeout, ret < 0 if interrupted */ @@ -249,22 +292,23 @@ int cmc221_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata) int ret = 0; int irq = 0; unsigned long flag = 0; - struct platform_device *pdev = NULL; - mc->gpio_cp_on = pdata->gpio_cp_on; - mc->gpio_cp_reset = pdata->gpio_cp_reset; + mc->gpio_cp_on = pdata->gpio_cp_on; + mc->gpio_cp_reset = pdata->gpio_cp_reset; mc->gpio_phone_active = pdata->gpio_phone_active; - mc->gpio_pda_active = pdata->gpio_pda_active; + 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_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; - mc->gpio_host_active = pdata->gpio_host_active; - mc->gpio_host_wakeup = pdata->gpio_host_wakeup; + mc->gpio_host_active = pdata->gpio_host_active; + mc->gpio_host_wakeup = pdata->gpio_host_wakeup; #endif - mc->gpio_dynamic_switching = pdata->gpio_dynamic_switching; + mc->gpio_link_switch = pdata->gpio_link_switch; mc->need_switch_to_usb = false; - +#ifdef CONFIG_EXYNOS4_CPUFREQ + mc->gpio_cpufreq_lock = pdata->gpio_cpufreq_lock; +#endif if (!mc->gpio_cp_on || !mc->gpio_cp_reset || !mc->gpio_phone_active) { mif_err("%s: ERR! no GPIO data\n", mc->name); return -ENXIO; @@ -276,10 +320,9 @@ int cmc221_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata) cmc221_get_ops(mc); dev_set_drvdata(mc->dev, mc); - pdev = to_platform_device(mc->dev); - mc->irq_phone_active = platform_get_irq_byname(pdev, "cp_active_irq"); + mc->irq_phone_active = pdata->irq_phone_active; if (!mc->irq_phone_active) { - mif_err("%s: ERR! get cp_active_irq fail\n", mc->name); + mif_err("%s: ERR! get irq_phone_active fail\n", mc->name); return -1; } mif_err("%s: PHONE_ACTIVE IRQ# = %d\n", mc->name, mc->irq_phone_active); @@ -301,8 +344,8 @@ int cmc221_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata) } flag = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_NO_SUSPEND; - if (mc->gpio_dynamic_switching) { - irq = gpio_to_irq(mc->gpio_dynamic_switching); + if (mc->gpio_link_switch) { + irq = gpio_to_irq(mc->gpio_link_switch); mif_err("%s: DYNAMIC_SWITCH IRQ# = %d\n", mc->name, irq); ret = request_irq(irq, dynamic_switching_handler, flag, "dynamic_switching", mc); @@ -313,5 +356,23 @@ int cmc221_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata) } } +#ifdef CONFIG_EXYNOS4_CPUFREQ + INIT_DELAYED_WORK(&mc->work_cpu_lock, cmc221_cpufreq_lock); + INIT_DELAYED_WORK(&mc->work_cpu_unlock, cmc221_cpufreq_unlock); + + flag = IRQF_TRIGGER_RISING; + if (mc->gpio_cpufreq_lock) { + irq = gpio_to_irq(mc->gpio_cpufreq_lock); + mif_err("%s: CPUFREQ_LOCK_CNT IRQ# = %d\n", mc->name, irq); + ret = request_irq(irq, cpufreq_lock_handler, flag, + "cpufreq_lock", mc); + if (ret) { + mif_err("%s: ERR! request_irq(#%d) fail (err %d)\n", + mc->name, irq, ret); + return ret; + } + } +#endif + return 0; } diff --git a/drivers/misc/modem_if/modem_modemctl_device_esc6270.c b/drivers/misc/modem_if/modem_modemctl_device_esc6270.c index 5a42755..79cdd95 100644 --- a/drivers/misc/modem_if/modem_modemctl_device_esc6270.c +++ b/drivers/misc/modem_if/modem_modemctl_device_esc6270.c @@ -24,7 +24,7 @@ #include <linux/sched.h> #include <linux/platform_device.h> -#include <linux/platform_data/modem.h> +#include "modem.h" #include "modem_prj.h" #include <linux/regulator/consumer.h> @@ -115,6 +115,13 @@ static int esc6270_reset(struct modem_ctl *mc) int esc6270_boot_on(struct modem_ctl *mc) { struct link_device *ld = get_current_link(mc->iod); +#if defined(CONFIG_LINK_DEVICE_DPRAM) + /* clear intr */ + struct dpram_link_device *dpld = to_dpram_link_device(ld); + u16 recv_msg = dpld->recv_intr(dpld); + + pr_info("[MODEM_IF:ESC] dpram intr: %x\n", recv_msg); +#endif pr_info("[MODEM_IF:ESC] <%s>\n", __func__); @@ -273,8 +280,11 @@ int esc6270_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata) 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"); + mc->irq_phone_active = pdata->irq_phone_active; + if (!mc->irq_phone_active) { + mif_err("%s: ERR! get irq_phone_active fail\n", mc->name); + return -1; + } pr_info("[MODEM_IF:ESC] <%s> PHONE_ACTIVE IRQ# = %d\n", __func__, mc->irq_phone_active); @@ -302,7 +312,7 @@ int esc6270_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata) } #if defined(CONFIG_SIM_DETECT) - mc->irq_sim_detect = platform_get_irq_byname(pdev, "sim_irq"); + mc->irq_sim_detect = pdata->irq_sim_detect; pr_info("[MODEM_IF:ESC] <%s> SIM_DECTCT IRQ# = %d\n", __func__, mc->irq_sim_detect); diff --git a/drivers/misc/modem_if/modem_modemctl_device_mdm6600.c b/drivers/misc/modem_if/modem_modemctl_device_mdm6600.c index ad44579..38bef9a 100644 --- a/drivers/misc/modem_if/modem_modemctl_device_mdm6600.c +++ b/drivers/misc/modem_if/modem_modemctl_device_mdm6600.c @@ -24,12 +24,14 @@ #include <linux/sched.h> #include <linux/platform_device.h> -#include <linux/platform_data/modem.h> +#include "modem.h" #include "modem_prj.h" #include <linux/regulator/consumer.h> #include <plat/gpio-cfg.h> +#include "modem_link_device_pld.h" + #if defined(CONFIG_MACH_M0_CTC) #include <linux/mfd/max77693.h> #endif @@ -39,6 +41,8 @@ static int mdm6600_on(struct modem_ctl *mc) { + struct link_device *ld = get_current_link(mc->iod); + pr_info("[MODEM_IF] mdm6600_on()\n"); if (!mc->gpio_cp_reset || !mc->gpio_cp_reset_msm || !mc->gpio_cp_on) { @@ -58,6 +62,7 @@ static int mdm6600_on(struct modem_ctl *mc) gpio_set_value(mc->gpio_pda_active, 1); mc->iod->modem_state_changed(mc->iod, STATE_BOOTING); + ld->mode = LINK_MODE_BOOT; return 0; } @@ -82,7 +87,8 @@ static int mdm6600_off(struct modem_ctl *mc) static int mdm6600_reset(struct modem_ctl *mc) { - int ret; + struct link_device *ld = get_current_link(mc->iod); + /* int ret; */ pr_info("[MODEM_IF] mdm6600_reset()\n"); @@ -109,6 +115,9 @@ static int mdm6600_reset(struct modem_ctl *mc) msleep(40); /* > 37.2 + 2 msec */ } + mc->iod->modem_state_changed(mc->iod, STATE_BOOTING); + ld->mode = LINK_MODE_BOOT; + return 0; } @@ -159,6 +168,7 @@ static irqreturn_t phone_active_irq_handler(int irq, void *_mc) int cp_dump_value = 0; int phone_state = 0; struct modem_ctl *mc = (struct modem_ctl *)_mc; + struct link_device *ld; if (!mc->gpio_cp_reset || !mc->gpio_phone_active /*|| !mc->gpio_cp_dump_int */) { @@ -179,11 +189,6 @@ static irqreturn_t phone_active_irq_handler(int irq, void *_mc) } else if (phone_reset && !phone_active_value) { if (count == 1) { phone_state = STATE_CRASH_EXIT; - if (mc->iod) { - ld = get_current_link(mc->iod); - if (ld->terminate_comm) - ld->terminate_comm(ld, mc->iod); - } if (mc->iod && mc->iod->modem_state_changed) mc->iod->modem_state_changed (mc->iod, phone_state); @@ -226,8 +231,11 @@ int mdm6600_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata) mc->vbus_on = pdata->vbus_on; mc->vbus_off = pdata->vbus_off; - pdev = to_platform_device(mc->dev); - mc->irq_phone_active = platform_get_irq_byname(pdev, "cp_active_irq"); + mc->irq_phone_active = pdata->irq_phone_active; + if (!mc->irq_phone_active) { + mif_err("%s: ERR! get irq_phone_active fail\n", mc->name); + return -1; + } pr_info("[MODEM_IF] <%s> PHONE_ACTIVE IRQ# = %d\n", __func__, mc->irq_phone_active); @@ -332,7 +340,7 @@ static int mdm6600_on(struct modem_ctl *mc) return -ENXIO; } - gpio_set_value(mc->gpio_pda_active, 0); + gpio_set_value(mc->gpio_pda_active, 1); gpio_set_value(mc->gpio_cp_on, 1); msleep(500); @@ -346,8 +354,6 @@ static int mdm6600_on(struct modem_ctl *mc) 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 @@ -420,9 +426,13 @@ static int mdm6600_reset(struct modem_ctl *mc) static int mdm6600_boot_on(struct modem_ctl *mc) { struct regulator *regulator; + struct link_device *ld = get_current_link(mc->iod); + struct pld_link_device *dpld = to_pld_link_device(ld); pr_info("[MSM] <%s>\n", __func__); + dpld->recv_intr(dpld); + if (!mc->gpio_flm_uart_sel) { pr_err("[MSM] no gpio data\n"); return -ENXIO; @@ -729,8 +739,11 @@ int mdm6600_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata) 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"); + mc->irq_phone_active = pdata->irq_phone_active; + if (!mc->irq_phone_active) { + mif_err("%s: ERR! get irq_phone_active fail\n", mc->name); + return -1; + } pr_info("[MSM] <%s> PHONE_ACTIVE IRQ# = %d\n", __func__, mc->irq_phone_active); @@ -754,7 +767,7 @@ int mdm6600_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata) } #if defined(CONFIG_SIM_DETECT) - mc->irq_sim_detect = platform_get_irq_byname(pdev, "sim_irq"); + mc->irq_sim_detect = pdata->irq_sim_detect; pr_info("[MSM] <%s> SIM_DECTCT IRQ# = %d\n", __func__, mc->irq_sim_detect); diff --git a/drivers/misc/modem_if/modem_modemctl_device_qsc6085.c b/drivers/misc/modem_if/modem_modemctl_device_qsc6085.c new file mode 100644 index 0000000..25d3cfe --- /dev/null +++ b/drivers/misc/modem_if/modem_modemctl_device_qsc6085.c @@ -0,0 +1,218 @@ +/* /linux/drivers/misc/modem_if/modem_modemctl_device_qsc6085.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/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 "modem.h" + +#include "modem_prj.h" +#include "modem_link_device_dpram.h" +#include "modem_utils.h" + +#define IDPRAM_NORMAL_BOOT_MAGIC 0x4D4E + +static irqreturn_t phone_active_irq_handler(int irq, void *arg) +{ + struct modem_ctl *mc = (struct modem_ctl *)arg; + int phone_reset = gpio_get_value(mc->gpio_cp_reset); + int phone_active = gpio_get_value(mc->gpio_phone_active); + int phone_state = mc->phone_state; + + pr_info("MIF: <%s> state = %d, phone_reset = %d, phone_active = %d\n", + __func__, phone_state, phone_reset, phone_active); + + if (phone_reset && phone_active) { + phone_state = STATE_ONLINE; + if (mc->phone_state != STATE_BOOTING) + 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; + 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("MIF: <%s> phone_state = %d\n", __func__, phone_state); + + return IRQ_HANDLED; +} + +static void set_idpram_boot_magic(struct dpram_link_device *dpld) +{ + dpld->set_access(dpld, 0); + dpld->set_magic(dpld, IDPRAM_NORMAL_BOOT_MAGIC); + dpld->set_access(dpld, 1); +} + +static int qsc6085_on(struct modem_ctl *mc) +{ + struct link_device *ld = get_current_link(mc->iod); + struct dpram_link_device *dpld = to_dpram_link_device(ld); + + mif_err("+++\n"); + + if (!mc->gpio_cp_on || !mc->gpio_cp_reset) { + mif_err("no gpio_cp_on or no gpio_cp_reset\n"); + return -ENXIO; + } + + set_idpram_boot_magic(dpld); + + mc->iod->modem_state_changed(mc->iod, STATE_BOOTING); + + gpio_set_value(mc->gpio_cp_reset, 1); + gpio_set_value(mc->gpio_cp_on, 0); + + msleep(100); + + gpio_set_value(mc->gpio_cp_on, 1); + + msleep(400); + msleep(400); + msleep(200); + + gpio_set_value(mc->gpio_cp_on, 0); + + mif_err("---\n"); + return 0; +} + +static int qsc6085_off(struct modem_ctl *mc) +{ + int phone_wait_cnt = 0; + + pr_info("MIF: <%s+>\n", __func__); + + if (!mc->gpio_cp_on || !mc->gpio_cp_reset || + !mc->gpio_phone_active) { + pr_err("MIF: <%s> no gpio data\n", __func__); + return -ENXIO; + } + + gpio_set_value(mc->gpio_cp_on, 0); + + /* confirm phone off */ + while (1) { + if (gpio_get_value(mc->gpio_phone_active)) { + pr_err("MIF: <%s> Try to Turn Phone Off by CP_RST\n", + __func__); + gpio_set_value(mc->gpio_cp_reset, 0); + if (phone_wait_cnt > 10) { + pr_emerg("MIF: <%s> OFF Failed\n", __func__); + break; + } + phone_wait_cnt++; + mdelay(100); + } else { + pr_emerg("MIF: <%s> OFF Success\n", __func__); + break; + } + } + + mc->iod->modem_state_changed(mc->iod, STATE_OFFLINE); + + pr_info("MIF: <%s->\n", __func__); + + return 0; +} + +static int qsc6085_reset(struct modem_ctl *mc) +{ + struct link_device *ld = get_current_link(mc->iod); + struct dpram_link_device *dpld = to_dpram_link_device(ld); + + mif_err("+++\n"); + + set_idpram_boot_magic(dpld); + + gpio_set_value(mc->gpio_cp_reset, 0); + msleep(100); + gpio_set_value(mc->gpio_cp_reset, 1); + + mif_err("---\n"); + return 0; +} + +static int qsc6085_modem_dump_reset(struct modem_ctl *mc) +{ + pr_info("MIF: <%s>\n", __func__); + panic("CP Crashed"); +} + +static void qsc6085_get_ops(struct modem_ctl *mc) +{ + mc->ops.modem_on = qsc6085_on; + mc->ops.modem_off = qsc6085_off; + mc->ops.modem_reset = qsc6085_reset; + mc->ops.modem_dump_reset = qsc6085_modem_dump_reset; +} + +int qsc6085_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata) +{ + int ret = 0; + unsigned long flag = 0; + + mc->gpio_cp_on = pdata->gpio_cp_on; + 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; + + if (!mc->gpio_cp_on || !mc->gpio_cp_reset || !mc->gpio_phone_active) { + mif_err("no GPIO data\n"); + return -ENXIO; + } + + mc->irq_phone_active = gpio_to_irq(mc->gpio_phone_active); + pr_err("MIF: <%s> PHONE_ACTIVE IRQ# = %d\n", + __func__, mc->irq_phone_active); + + qsc6085_get_ops(mc); + + /*register phone_active_handler*/ + flag = IRQF_TRIGGER_HIGH; + + ret = request_irq(mc->irq_phone_active, + phone_active_irq_handler, + flag, "phone_active", mc); + if (ret) { + pr_err("MIF: failed to irq_phone_active request_irq: %d\n" + , ret); + return ret; + } + + ret = enable_irq_wake(mc->irq_phone_active); + if (ret) { + pr_err("MIF: <%s> failed to enable_irq_wake:%d\n", + __func__, ret); + free_irq(mc->irq_phone_active, mc); + return ret; + } + return ret; +} diff --git a/drivers/misc/modem_if/modem_modemctl_device_sprd8803.c b/drivers/misc/modem_if/modem_modemctl_device_sprd8803.c index cfa2896..7a6e6fc 100644 --- a/drivers/misc/modem_if/modem_modemctl_device_sprd8803.c +++ b/drivers/misc/modem_if/modem_modemctl_device_sprd8803.c @@ -22,10 +22,13 @@ #include <linux/platform_device.h> #include <linux/cma.h> #include <plat/devs.h> -#include <linux/platform_data/modem.h> +#include "modem.h" #include "modem_prj.h" #include <plat/gpio-cfg.h> +spinlock_t irq_lock; +int irq_lock_flag; + int sprd_boot_done; extern int spi_thread_restart(void); @@ -45,10 +48,20 @@ static int sprd8803_on(struct modem_ctl *mc) gpio_set_value(mc->gpio_cp_ctrl2, 1); #endif msleep(100); -// pr_info("[MODEM_IF] %s\n", __func__); // Kill spam + pr_info("[MODEM_IF] %s\n", __func__); gpio_set_value(mc->gpio_cp_on, 1); gpio_set_value(mc->gpio_pda_active, 1); + spin_lock(&irq_lock); + if (!irq_lock_flag) { + enable_irq(mc->irq_phone_active); + enable_irq(gpio_to_irq(mc->gpio_cp_dump_int)); + enable_irq_wake(mc->irq_phone_active); + enable_irq_wake(gpio_to_irq(mc->gpio_cp_dump_int)); + irq_lock_flag = 1; + } + spin_unlock(&irq_lock); + mc->phone_state = STATE_BOOTING; return 0; @@ -56,7 +69,7 @@ static int sprd8803_on(struct modem_ctl *mc) static int sprd8803_off(struct modem_ctl *mc) { - pr_debug("[MODEM_IF] %s\n", __func__); + pr_info("[MODEM_IF] %s\n", __func__); if (!mc->gpio_cp_on) { mif_err("no gpio data\n"); @@ -64,6 +77,17 @@ static int sprd8803_off(struct modem_ctl *mc) } gpio_set_value(mc->gpio_cp_on, 0); + gpio_set_value(mc->gpio_pda_active, 0); + + spin_lock(&irq_lock); + if (irq_lock_flag) { + disable_irq(mc->irq_phone_active); + disable_irq(gpio_to_irq(mc->gpio_cp_dump_int)); + disable_irq_wake(mc->irq_phone_active); + disable_irq_wake(gpio_to_irq(mc->gpio_cp_dump_int)); + irq_lock_flag = 0; + } + spin_unlock(&irq_lock); mc->phone_state = STATE_OFFLINE; @@ -72,7 +96,7 @@ static int sprd8803_off(struct modem_ctl *mc) static int sprd8803_reset(struct modem_ctl *mc) { - pr_debug("[MODEM_IF] %s\n", __func__); + pr_info("[MODEM_IF] %s\n", __func__); spi_thread_restart(); @@ -81,20 +105,20 @@ static int sprd8803_reset(struct modem_ctl *mc) static int sprd8803_boot_on(struct modem_ctl *mc) { - pr_debug("[MODEM_IF] %s %d\n", __func__, sprd_boot_done); - return sprd_boot_done; + pr_info("[MODEM_IF] %s %d\n", __func__, mc->phone_state); + return mc->phone_state; } static int sprd8803_boot_off(struct modem_ctl *mc) { - pr_debug("[MODEM_IF] %s\n", __func__); + pr_info("[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__); + pr_info("[MODEM_IF] %s\n", __func__); if (!mc->gpio_ap_cp_int2) return -ENXIO; @@ -115,6 +139,8 @@ static irqreturn_t phone_active_irq_handler(int irq, void *_mc) int phone_state = 0; struct modem_ctl *mc = (struct modem_ctl *)_mc; + disable_irq_nosync(mc->irq_phone_active); + if (!mc->gpio_phone_active || !mc->gpio_cp_dump_int) { pr_err("[MODEM_IF] no gpio data\n"); @@ -135,7 +161,7 @@ static irqreturn_t phone_active_irq_handler(int irq, void *_mc) else phone_state = STATE_OFFLINE; - if (cp_dump_value) + if (phone_active_value && cp_dump_value) phone_state = STATE_CRASH_EXIT; if (mc->iod && mc->iod->modem_state_changed) @@ -145,6 +171,8 @@ static irqreturn_t phone_active_irq_handler(int irq, void *_mc) mc->bootd->modem_state_changed(mc->bootd, phone_state); exit: + enable_irq(mc->irq_phone_active); + return IRQ_HANDLED; } @@ -217,5 +245,19 @@ int sprd8803_init_modemctl_device(struct modem_ctl *mc, __func__, ret); free_irq(irq_cp_dump_int, mc); } + + irq_lock_flag = 1; + spin_lock_init(&irq_lock); + + spin_lock(&irq_lock); + if (irq_lock_flag) { + disable_irq(mc->irq_phone_active); + disable_irq(gpio_to_irq(mc->gpio_cp_dump_int)); + disable_irq_wake(mc->irq_phone_active); + disable_irq_wake(gpio_to_irq(mc->gpio_cp_dump_int)); + irq_lock_flag = 0; + } + spin_unlock(&irq_lock); + return ret; } diff --git a/drivers/misc/modem_if/modem_modemctl_device_ss222.c b/drivers/misc/modem_if/modem_modemctl_device_ss222.c new file mode 100644 index 0000000..5dbda45 --- /dev/null +++ b/drivers/misc/modem_if/modem_modemctl_device_ss222.c @@ -0,0 +1,312 @@ +/* + * 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 <mach/c2c.h> +#include "modem.h" +#include "modem_prj.h" +#include "modem_utils.h" + +#define MIF_INIT_TIMEOUT (30 * HZ) + +static void ss222_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); + int old_state = mc->phone_state; + int new_state = mc->phone_state; + + mif_err("old_state:%s cp_on:%d cp_reset:%d cp_active:%d\n", + get_cp_state_str(old_state), cp_on, cp_reset, cp_active); + + if (cp_active) { + if (!cp_on) { + new_state = STATE_OFFLINE; + ld->mode = LINK_MODE_OFFLINE; + } else if (old_state == STATE_ONLINE) { + new_state = STATE_CRASH_EXIT; + ld->mode = LINK_MODE_ULOAD; + } else { + mif_err("don't care!!!\n"); + } + } + + if (old_state != new_state) { + mif_err("new_state = %s\n", get_cp_state_str(new_state)); + mc->bootd->modem_state_changed(mc->bootd, new_state); + mc->iod->modem_state_changed(mc->iod, new_state); + } +} + +static irqreturn_t phone_active_handler(int irq, void *arg) +{ + struct modem_ctl *mc = (struct modem_ctl *)arg; + int cp_reset = gpio_get_value(mc->gpio_cp_reset); + + if (cp_reset) + ss222_mc_state_fsm(mc); + + return IRQ_HANDLED; +} + +static inline void make_gpio_floating(int gpio, bool floating) +{ + if (floating) + gpio_direction_input(gpio); + else + gpio_direction_output(gpio, 0); +} + +static int ss222_on(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_off = gpio_get_value(mc->gpio_cp_off); + int cp_reset = gpio_get_value(mc->gpio_cp_reset); + int cp_active = gpio_get_value(mc->gpio_phone_active); + int cp_status = gpio_get_value(mc->gpio_cp_status); + mif_err("+++\n"); + mif_err("cp_on:%d cp_reset:%d ps_hold:%d cp_active:%d cp_status:%d\n", + cp_on, cp_reset, cp_off, cp_active, cp_status); + + gpio_set_value(mc->gpio_pda_active, 1); + + if (!wake_lock_active(&mc->mc_wake_lock)) + wake_lock(&mc->mc_wake_lock); + + mc->phone_state = STATE_OFFLINE; + ld->mode = LINK_MODE_OFFLINE; + + /* Make PS_HOLD floating (Hi-Z) for CP ON */ + make_gpio_floating(mc->gpio_cp_off, true); + + 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); + + c2c_reload(); + gpio_set_value(mc->gpio_cp_reset, 1); + msleep(300); + + mif_err("---\n"); + return 0; +} + +static int ss222_off(struct modem_ctl *mc) +{ + struct link_device *ld = get_current_link(mc->iod); + int cp_on = gpio_get_value(mc->gpio_cp_on); + mif_err("+++\n"); + + if (mc->phone_state == STATE_OFFLINE || cp_on == 0) + return 0; + + mc->phone_state = STATE_OFFLINE; + ld->mode = LINK_MODE_OFFLINE; + + gpio_set_value(mc->gpio_cp_reset, 0); + + /* Make PS_HOLD LOW for CP OFF */ + make_gpio_floating(mc->gpio_cp_off, false); + gpio_set_value(mc->gpio_cp_on, 0); + + mif_err("---\n"); + return 0; +} + +static int ss222_reset(struct modem_ctl *mc) +{ + mif_err("+++\n"); + + if (ss222_off(mc)) + return -EIO; + + msleep(100); + + if (ss222_on(mc)) + return -EIO; + + mif_err("---\n"); + return 0; +} + +static int ss222_force_crash_exit(struct modem_ctl *mc) +{ + struct link_device *ld = get_current_link(mc->bootd); + mif_err("+++\n"); + + /* Make DUMP start */ + ld->force_dump(ld, mc->bootd); + + mif_err("---\n"); + return 0; +} + +static int ss222_dump_reset(struct modem_ctl *mc) +{ + unsigned int gpio_cp_reset = mc->gpio_cp_reset; + mif_err("+++\n"); + + if (!wake_lock_active(&mc->mc_wake_lock)) + wake_lock(&mc->mc_wake_lock); + + gpio_set_value(gpio_cp_reset, 0); + udelay(200); + + c2c_reload(); + gpio_set_value(gpio_cp_reset, 1); + msleep(300); + + gpio_set_value(mc->gpio_ap_status, 1); + + mif_err("---\n"); + return 0; +} + +static int ss222_boot_on(struct modem_ctl *mc) +{ + struct link_device *ld = get_current_link(mc->bootd); + mif_debug("+++\n"); + + disable_irq_nosync(mc->irq_phone_active); + + gpio_set_value(mc->gpio_ap_status, 1); + + ld->mode = LINK_MODE_BOOT; + + mc->bootd->modem_state_changed(mc->bootd, STATE_BOOTING); + mc->iod->modem_state_changed(mc->iod, STATE_BOOTING); + + INIT_COMPLETION(ld->init_cmpl); + + mif_debug("---\n"); + return 0; +} + +static int ss222_boot_off(struct modem_ctl *mc) +{ + struct link_device *ld = get_current_link(mc->bootd); + unsigned long remain; + mif_debug("+++\n"); + + ld->mode = LINK_MODE_IPC; + + remain = wait_for_completion_timeout(&ld->init_cmpl, MIF_INIT_TIMEOUT); + if (remain == 0) { + mif_err("T-I-M-E-O-U-T\n"); + mif_err("xxx\n"); + return -EAGAIN; + } + + mif_debug("---\n"); + return 0; +} + +static int ss222_boot_done(struct modem_ctl *mc) +{ + mif_debug("+++\n"); + + if (wake_lock_active(&mc->mc_wake_lock)) + wake_unlock(&mc->mc_wake_lock); + + enable_irq(mc->irq_phone_active); + + mif_debug("---\n"); + return 0; +} + +static void ss222_get_ops(struct modem_ctl *mc) +{ + mc->ops.modem_on = ss222_on; + mc->ops.modem_off = ss222_off; + mc->ops.modem_reset = ss222_reset; + mc->ops.modem_boot_on = ss222_boot_on; + mc->ops.modem_boot_off = ss222_boot_off; + mc->ops.modem_boot_done = ss222_boot_done; + mc->ops.modem_force_crash_exit = ss222_force_crash_exit; + mc->ops.modem_dump_reset = ss222_dump_reset; +} + +int ss222_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata) +{ + int ret = 0; + int irq = 0; + unsigned long flag = 0; + mif_debug("+++\n"); + + if (!pdata->gpio_cp_on || !pdata->gpio_cp_off || !pdata->gpio_cp_reset + || !pdata->gpio_pda_active || !pdata->gpio_phone_active + || !pdata->gpio_ap_wakeup || !pdata->gpio_ap_status + || !pdata->gpio_cp_wakeup || !pdata->gpio_cp_status) { + mif_err("ERR! no GPIO data\n"); + mif_err("xxx\n"); + return -ENXIO; + } + + mc->gpio_cp_on = pdata->gpio_cp_on; + mc->gpio_cp_off = pdata->gpio_cp_off; + 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_ap_wakeup = pdata->gpio_ap_wakeup; + mc->gpio_ap_status = pdata->gpio_ap_status; + mc->gpio_cp_wakeup = pdata->gpio_cp_wakeup; + mc->gpio_cp_status = pdata->gpio_cp_status; + + gpio_set_value(mc->gpio_cp_reset, 0); + + gpio_set_value(mc->gpio_cp_on, 0); + + ss222_get_ops(mc); + dev_set_drvdata(mc->dev, mc); + + wake_lock_init(&mc->mc_wake_lock, WAKE_LOCK_SUSPEND, "umts_wake_lock"); + + mc->irq_phone_active = pdata->irq_phone_active; + if (!mc->irq_phone_active) { + mif_err("ERR! no irq_phone_active\n"); + mif_err("xxx\n"); + return -1; + } + mif_err("PHONE_ACTIVE IRQ# = %d\n", mc->irq_phone_active); + + irq = mc->irq_phone_active; + flag = IRQF_TRIGGER_RISING | IRQF_NO_SUSPEND; + ret = request_irq(irq, phone_active_handler, flag, "umts_active", mc); + if (ret) { + mif_err("ERR! request_irq(#%d) fail (err %d)\n", irq, ret); + mif_err("xxx\n"); + return ret; + } + ret = enable_irq_wake(irq); + if (ret) + mif_err("enable_irq_wake(#%d) fail (err %d)\n", irq, ret); + + mif_debug("---\n"); + return 0; +} + diff --git a/drivers/misc/modem_if/modem_modemctl_device_xmm6260.c b/drivers/misc/modem_if/modem_modemctl_device_xmm6260.c index d2fcf5b..9741f59 100644 --- a/drivers/misc/modem_if/modem_modemctl_device_xmm6260.c +++ b/drivers/misc/modem_if/modem_modemctl_device_xmm6260.c @@ -22,12 +22,12 @@ #include <linux/delay.h> #include <linux/platform_device.h> -#include <linux/platform_data/modem.h> +#include "modem.h" #include "modem_prj.h" static int xmm6260_on(struct modem_ctl *mc) { - mif_debug("xmm6260_on()\n"); + mif_info("xmm6260_on()\n"); if (!mc->gpio_cp_reset || !mc->gpio_cp_on || !mc->gpio_reset_req_n) { mif_err("no gpio data\n"); @@ -66,7 +66,7 @@ static int xmm6260_on(struct modem_ctl *mc) static int xmm6260_off(struct modem_ctl *mc) { - mif_debug("xmm6260_off()\n"); + mif_info("xmm6260_off()\n"); if (!mc->gpio_cp_reset || !mc->gpio_cp_on) { mif_err("no gpio data\n"); @@ -85,7 +85,7 @@ static int xmm6260_off(struct modem_ctl *mc) static int xmm6260_reset(struct modem_ctl *mc) { - mif_debug("xmm6260_reset()\n"); + mif_info("xmm6260_reset()\n"); if (!mc->gpio_cp_reset || !mc->gpio_reset_req_n) return -ENXIO; @@ -122,7 +122,7 @@ static int xmm6260_reset(struct modem_ctl *mc) static int xmm6260_boot_on(struct modem_ctl *mc) { - mif_debug("xmm6260_boot_on()\n"); + mif_info("xmm6260_boot_on()\n"); if (!mc->gpio_flm_uart_sel) { mif_err("no gpio data\n"); @@ -136,7 +136,7 @@ static int xmm6260_boot_on(struct modem_ctl *mc) static int xmm6260_boot_off(struct modem_ctl *mc) { - mif_debug("xmm6260_boot_off()\n"); + mif_info("xmm6260_boot_off()\n"); if (!mc->gpio_flm_uart_sel) { mif_err("no gpio data\n"); diff --git a/drivers/misc/modem_if/modem_modemctl_device_xmm6262.c b/drivers/misc/modem_if/modem_modemctl_device_xmm6262.c index 4d0b69c..5473aad 100644 --- a/drivers/misc/modem_if/modem_modemctl_device_xmm6262.c +++ b/drivers/misc/modem_if/modem_modemctl_device_xmm6262.c @@ -24,9 +24,11 @@ #include <linux/platform_device.h> #include <linux/cma.h> #include <plat/devs.h> -#include <linux/platform_data/modem.h> +#include "modem.h" #include "modem_prj.h" - +#ifdef CONFIG_FAST_BOOT +#include <linux/fake_shut_down.h> +#endif static int xmm6262_on(struct modem_ctl *mc) { mif_info("\n"); @@ -59,11 +61,13 @@ static int xmm6262_on(struct modem_ctl *mc) udelay(60); gpio_set_value(mc->gpio_cp_on, 0); msleep(20); + + mc->phone_state = STATE_BOOTING; + if (mc->gpio_revers_bias_restore) mc->gpio_revers_bias_restore(); gpio_set_value(mc->gpio_pda_active, 1); - mc->phone_state = STATE_BOOTING; return 0; } @@ -120,6 +124,20 @@ static int xmm6262_reset(struct modem_ctl *mc) return 0; } +static int xmm6262_force_crash_exit(struct modem_ctl *mc) +{ + mif_info("\n"); + + if (!mc->gpio_ap_dump_int) + return -ENXIO; + + gpio_set_value(mc->gpio_ap_dump_int, 1); + mif_info("set ap_dump_int(%d) to high=%d\n", + mc->gpio_ap_dump_int, gpio_get_value(mc->gpio_ap_dump_int)); + return 0; +} + + static irqreturn_t phone_active_irq_handler(int irq, void *_mc) { int phone_reset = 0; @@ -172,14 +190,72 @@ static irqreturn_t phone_active_irq_handler(int irq, void *_mc) return IRQ_HANDLED; } +#ifdef CONFIG_FAST_BOOT +#include <linux/reboot.h> + +static void mif_sim_detect_complete(struct modem_ctl *mc) +{ + if (mc->sim_shutdown_req) { + mif_info("fake shutdown sim changed shutdown\n"); + kernel_power_off(); + /*kernel_restart(NULL);*/ + mc->sim_shutdown_req = false; + } +} + +static int mif_init_sim_shutdown(struct modem_ctl *mc) +{ + mc->sim_shutdown_req = false; + mc->modem_complete = mif_sim_detect_complete; + + return 0; +} + +static void mif_check_fake_shutdown(struct modem_ctl *mc, bool online) +{ + if (fake_shut_down && mc->sim_state.online != online) + mc->sim_shutdown_req = true; +} + +#else +static inline int mif_init_sim_shutdown(struct modem_ctl *mc) { return 0; } +#define mif_check_fake_shutdown(a, b) do {} while (0) +#endif + + +#define SIM_DETECT_DEBUG static irqreturn_t sim_detect_irq_handler(int irq, void *_mc) { struct modem_ctl *mc = (struct modem_ctl *)_mc; +#ifdef SIM_DETECT_DEBUG + int val = gpio_get_value(mc->gpio_sim_detect); + static int unchange; + static int prev_val; + + if (mc->phone_state == STATE_BOOTING) { + mif_info("BOOTING, reset unchange\n"); + unchange = 0; + } - if (mc->iod && mc->iod->sim_state_changed) + if (prev_val == val) { + if (unchange++ > 50) { + mif_err("Abnormal SIM detect GPIO irqs"); + disable_irq_nosync(mc->gpio_sim_detect); + panic("SIM detect IRQ Error"); + } + } else { + unchange = 0; + } + prev_val = val; +#endif + if (mc->iod && mc->iod->sim_state_changed) { + mif_check_fake_shutdown(mc, + gpio_get_value(mc->gpio_sim_detect) == mc->sim_polarity + ); mc->iod->sim_state_changed(mc->iod, gpio_get_value(mc->gpio_sim_detect) == mc->sim_polarity ); + } return IRQ_HANDLED; } @@ -189,6 +265,7 @@ static void xmm6262_get_ops(struct modem_ctl *mc) mc->ops.modem_on = xmm6262_on; mc->ops.modem_off = xmm6262_off; mc->ops.modem_reset = xmm6262_reset; + mc->ops.modem_force_crash_exit = xmm6262_force_crash_exit; } int xmm6262_init_modemctl_device(struct modem_ctl *mc, @@ -218,6 +295,7 @@ int xmm6262_init_modemctl_device(struct modem_ctl *mc, 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); @@ -261,6 +339,12 @@ 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_polarity; + + ret = mif_init_sim_shutdown(mc); + if (ret) { + mif_err("failed to sim fake shutdown init: %d\n", ret); + goto err_sim_detect_set_wake_irq; + } } return ret; diff --git a/drivers/misc/modem_if/modem_net_flowcontrol_device.c b/drivers/misc/modem_if/modem_net_flowcontrol_device.c index b3f055d..3aa340f 100644 --- a/drivers/misc/modem_if/modem_net_flowcontrol_device.c +++ b/drivers/misc/modem_if/modem_net_flowcontrol_device.c @@ -22,7 +22,7 @@ #include <linux/sched.h> #include <linux/netdevice.h> #include <linux/if_arp.h> -#include <linux/platform_data/modem.h> +#include "modem.h" #include "modem_prj.h" diff --git a/drivers/misc/modem_if/modem_prj.h b/drivers/misc/modem_if/modem_prj.h index f85596f..ccff272 100644 --- a/drivers/misc/modem_if/modem_prj.h +++ b/drivers/misc/modem_if/modem_prj.h @@ -1,5 +1,4 @@ /* - * Copyright (C) 2010 Google, Inc. * Copyright (C) 2010 Samsung Electronics. * * This software is licensed under the terms of the GNU General Public @@ -19,12 +18,18 @@ #include <linux/wait.h> #include <linux/miscdevice.h> #include <linux/skbuff.h> +#include <linux/interrupt.h> #include <linux/completion.h> #include <linux/wakelock.h> #include <linux/rbtree.h> #include <linux/spinlock.h> #include <linux/cdev.h> #include <linux/types.h> +#include "modem.h" + +#ifndef CONFIG_SAMSUNG_PRODUCT_SHIP +#define DEBUG_MODEM_IF +#endif #define MAX_CPINFO_SIZE 512 @@ -33,7 +38,10 @@ #define MAX_FMT_DEVS 10 #define MAX_RAW_DEVS 32 #define MAX_RFS_DEVS 10 -#define MAX_NUM_IO_DEV (MAX_FMT_DEVS + MAX_RAW_DEVS + MAX_RFS_DEVS) +#define MAX_BOOT_DEVS 10 +#define MAX_DUMP_DEVS 10 + +#define MAX_IOD_RXQ_LEN 2048 #define IOCTL_MODEM_ON _IO('o', 0x19) #define IOCTL_MODEM_OFF _IO('o', 0x20) @@ -62,22 +70,40 @@ #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) - -/* ioctl command definitions. */ -#define IOCTL_DPRAM_PHONE_POWON _IO('o', 0xd0) -#define IOCTL_DPRAM_PHONEIMG_LOAD _IO('o', 0xd1) -#define IOCTL_DPRAM_NVDATA_LOAD _IO('o', 0xd2) -#define IOCTL_DPRAM_PHONE_BOOTSTART _IO('o', 0xd3) +#define IOCTL_MODEM_RAMDUMP_START _IO('o', 0xCE) +#define IOCTL_MODEM_RAMDUMP_STOP _IO('o', 0xCF) -#define IOCTL_DPRAM_PHONE_UPLOAD_STEP1 _IO('o', 0xde) -#define IOCTL_DPRAM_PHONE_UPLOAD_STEP2 _IO('o', 0xdf) +#define IOCTL_MODEM_XMIT_BOOT _IO('o', 0x40) +#define IOCTL_DPRAM_INIT_STATUS _IO('o', 0x43) /* ioctl command for IPC Logger */ #define IOCTL_MIF_LOG_DUMP _IO('o', 0x51) #define IOCTL_MIF_DPRAM_DUMP _IO('o', 0x52) +/* ioctl command definitions. */ +#define IOCTL_DPRAM_PHONE_POWON _IO('o', 0xD0) +#define IOCTL_DPRAM_PHONEIMG_LOAD _IO('o', 0xD1) +#define IOCTL_DPRAM_NVDATA_LOAD _IO('o', 0xD2) +#define IOCTL_DPRAM_PHONE_BOOTSTART _IO('o', 0xD3) + +#define IOCTL_DPRAM_PHONE_UPLOAD_STEP1 _IO('o', 0xDE) +#define IOCTL_DPRAM_PHONE_UPLOAD_STEP2 _IO('o', 0xDF) + +#define CPBOOT_DIR_MASK 0xF000 +#define CPBOOT_STAGE_MASK 0x0F00 +#define CPBOOT_CMD_MASK 0x000F +#define CPBOOT_REQ_RESP_MASK 0x0FFF + +#define CPBOOT_DIR_AP2CP 0x9000 +#define CPBOOT_DIR_CP2AP 0xA000 + +#define CPBOOT_STAGE_SHIFT 8 + +#define CPBOOT_STAGE_START 0x0000 +#define CPBOOT_CRC_SEND 0x000C +#define CPBOOT_STAGE_DONE 0x000D +#define CPBOOT_STAGE_FAIL 0x000F + /* modem status */ #define MODEM_OFF 0 #define MODEM_CRASHED 1 @@ -93,83 +119,50 @@ #define PSD_DATA_CHID_BEGIN 0x2A #define PSD_DATA_CHID_END 0x38 -#define PS_DATA_CH_0 10 -#define PS_DATA_CH_LAST 24 +#define PS_DATA_CH_0 10 +#define PS_DATA_CH_LAST 24 +#define RMNET0_CH_ID PS_DATA_CH_0 #define IP6VERSION 6 #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 +/* IP loopback */ +#define DATA_DRAIN_CHANNEL 30 /* Drain channel to drop RX packets */ #define DATA_LOOPBACK_CHANNEL 31 /* Debugging features */ -#define MAX_MIF_LOG_PATH_LEN 128 -#define MAX_MIF_LOG_FILE_SIZE 0x800000 /* 8 MB */ - -#define MAX_MIF_EVT_BUFF_SIZE 256 -#define MAX_MIF_TIME_LEN 32 -#define MAX_MIF_NAME_LEN 16 -#define MAX_MIF_STR_LEN 127 -#define MAX_MIF_LOG_LEN 128 - -enum mif_event_id { - MIF_IRQ_EVT = 0, - MIF_LNK_RX_EVT, - MIF_MUX_RX_EVT, - MIF_IOD_RX_EVT, - MIF_IOD_TX_EVT, - MIF_MUX_TX_EVT, - MIF_LNK_TX_EVT, - MAX_MIF_EVT -}; - -struct dpram_queue_status { - unsigned in; - unsigned out; -}; - -struct dpram_queue_status_pair { - struct dpram_queue_status txq; - struct dpram_queue_status rxq; -}; - -struct dpram_irq_buff { - unsigned magic; - unsigned access; - struct dpram_queue_status_pair qsp[MAX_IPC_DEV]; - unsigned int2ap; - unsigned int2cp; -}; - -/* Not use */ -struct mif_event_buff { - char time[MAX_MIF_TIME_LEN]; - - struct timeval tv; - enum mif_event_id evt; - - char mc[MAX_MIF_NAME_LEN]; - - char iod[MAX_MIF_NAME_LEN]; - - char ld[MAX_MIF_NAME_LEN]; - enum modem_link link_type; - - unsigned rcvd; - unsigned len; - union { - u8 data[MAX_MIF_LOG_LEN]; - struct dpram_irq_buff dpram_irqb; - }; +#define MIF_LOG_DIR "/sdcard/log" +#define MIF_MAX_PATH_LEN 256 +#define MIF_MAX_NAME_LEN 64 +#define MIF_MAX_STR_LEN 32 + +#define CP_CRASH_TAG "CP Crash " + +static const char const *dev_format_str[] = { + [IPC_FMT] = "FMT", + [IPC_RAW] = "RAW", + [IPC_RFS] = "RFS", + [IPC_MULTI_RAW] = "MULTI_RAW", + [IPC_CMD] = "CMD", + [IPC_BOOT] = "BOOT", + [IPC_RAMDUMP] = "RAMDUMP", + [IPC_DEBUG] = "DEBUG", }; -#define MIF_LOG_DIR "/sdcard" -#define MIF_LOG_LV_FILE "/data/.mif_log_level" +/** + * get_dev_name + * @dev: IPC device (enum dev_format) + * + * Returns IPC device name as a string. + */ +static const inline char *get_dev_name(unsigned int dev) +{ + if (unlikely(dev >= MAX_DEV_FORMAT)) + return "INVALID"; + else + return dev_format_str[dev]; +} /* Does modem ctl structure will use state ? or status defined below ?*/ enum modem_state { @@ -187,6 +180,26 @@ enum modem_state { #endif }; +static const char const *cp_state_str[] = { + [STATE_OFFLINE] = "OFFLINE", + [STATE_CRASH_RESET] = "CRASH_RESET", + [STATE_CRASH_EXIT] = "CRASH_EXIT", + [STATE_BOOTING] = "BOOTING", + [STATE_ONLINE] = "ONLINE", + [STATE_NV_REBUILDING] = "NV_REBUILDING", + [STATE_LOADER_DONE] = "LOADER_DONE", + [STATE_SIM_ATTACH] = "SIM_ATTACH", + [STATE_SIM_DETACH] = "SIM_DETACH", +#if defined(CONFIG_SEC_DUAL_MODEM_MODE) + [STATE_MODEM_SWITCH] = "MODEM_SWITCH", +#endif +}; + +static const inline char *get_cp_state_str(int state) +{ + return cp_state_str[state]; +} + enum com_state { COM_NONE, COM_ONLINE, @@ -208,6 +221,17 @@ struct sim_state { bool changed; /* online is changed? */ }; +enum cp_boot_mode { + CP_BOOT_MODE_NORMAL, + CP_BOOT_MODE_DUMP, + MAX_CP_BOOT_MODE +}; + +struct modem_firmware { + char *binary; + u32 size; +}; + #define HDLC_START 0x7F #define HDLC_END 0x7E #define SIZE_OF_HDLC_START 1 @@ -216,8 +240,8 @@ struct sim_state { struct header_data { char hdr[HDLC_HEADER_MAX_SIZE]; - unsigned len; - unsigned frag_len; + u32 len; + u32 frag_len; char start; /*hdlc start header 0x7F*/ }; @@ -255,10 +279,14 @@ struct sipc_fmt_hdr { #define SIPC5_EXT_FIELD_EXIST 0b00000010 #define SIPC5_CTL_FIELD_EXIST 0b00000001 -#define SIPC5_MAX_HEADER_SIZE 6 -#define SIPC5_HEADER_SIZE_WITH_EXT_LEN 6 +#define SIPC5_EXT_LENGTH_MASK SIPC5_EXT_FIELD_EXIST +#define SIPC5_CTL_FIELD_MASK (SIPC5_EXT_FIELD_EXIST | SIPC5_CTL_FIELD_EXIST) + +#define SIPC5_MIN_HEADER_SIZE 4 #define SIPC5_HEADER_SIZE_WITH_CTL_FLD 5 -#define SIPC5_MIN_HEADER_SIZE 4 +#define SIPC5_HEADER_SIZE_WITH_EXT_LEN 6 +#define SIPC5_MAX_HEADER_SIZE SIPC5_HEADER_SIZE_WITH_EXT_LEN + #define SIPC5_CONFIG_SIZE 1 #define SIPC5_CH_ID_SIZE 1 @@ -267,13 +295,18 @@ struct sipc_fmt_hdr { #define SIPC5_LEN_OFFSET 2 #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_BOOT0 215 +#define SIPC5_CH_ID_DUMP0 225 #define SIPC5_CH_ID_FMT_0 235 #define SIPC5_CH_ID_RFS_0 245 #define SIPC5_CH_ID_MAX 255 +#define SIPC5_CH_ID_FLOW_CTRL 255 +#define FLOW_CTRL_SUSPEND ((u8)(0xCA)) +#define FLOW_CTRL_RESUME ((u8)(0xCB)) + /* If iod->id is 0, do not need to store to `iodevs_tree_fmt' in SIPC4 */ #define sipc4_is_not_reserved_channel(ch) ((ch) != 0) @@ -295,20 +328,6 @@ struct sipc5_link_hdr { } __packed; struct sipc5_frame_data { - /* Config octet */ - u8 config; - - /* Channel ID */ - u8 ch_id; - - /* Control for multiple FMT frame */ - u8 control; - - /* Frame configuration set by header analysis */ - bool padding; - bool ctl_fld; - bool ext_len; - /* Frame length calculated from the length fields */ unsigned len; @@ -318,11 +337,17 @@ struct sipc5_frame_data { /* The length of received header */ unsigned hdr_rcvd; - /* The length of data payload */ - unsigned data_len; + /* The length of link layer payload */ + unsigned pay_len; /* The length of received data */ - unsigned data_rcvd; + unsigned pay_rcvd; + + /* The length of link layer padding */ + unsigned pad_len; + + /* The length of received padding */ + unsigned pad_rcvd; /* Header buffer */ u8 hdr[SIPC5_MAX_HEADER_SIZE]; @@ -349,8 +374,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; + + /* for indicating that thers is only one IPC frame in an skb */ + bool single_frame; } __packed; static inline struct skbuff_private *skbpriv(struct sk_buff *skb) @@ -359,6 +385,35 @@ static inline struct skbuff_private *skbpriv(struct sk_buff *skb) return (struct skbuff_private *)&skb->cb; } +enum iod_rx_state { + IOD_RX_ON_STANDBY = 0, + IOD_RX_HEADER, + IOD_RX_PAYLOAD, + IOD_RX_PADDING, + MAX_IOD_RX_STATE +}; + +static const char const *rx_state_str[] = { + [IOD_RX_ON_STANDBY] = "RX_ON_STANDBY", + [IOD_RX_HEADER] = "RX_HEADER", + [IOD_RX_PAYLOAD] = "RX_PAYLOAD", + [IOD_RX_PADDING] = "RX_PADDING", +}; + +/** + * get_dev_name + * @dev: IPC device (enum dev_format) + * + * Returns IPC device name as a string. + */ +static const inline char *get_rx_state_str(unsigned int state) +{ + if (unlikely(state >= MAX_IOD_RX_STATE)) + return "INVALID_STATE"; + else + return rx_state_str[state]; +} + struct io_device { /* rb_tree node for an io device */ struct rb_node node_chan; @@ -367,6 +422,7 @@ struct io_device { /* Name of the IO device */ char *name; + /* Reference count */ atomic_t opened; /* Wait queue for the IO device */ @@ -383,7 +439,11 @@ struct io_device { enum modem_io io_typ; enum modem_network net_typ; - bool use_handover; /* handover 2+ link devices */ + /* The name of the application that will use this IO device */ + char *app; + + /* Whether or not handover among 2+ link devices */ + bool use_handover; /* SIPC version */ enum sipc_ver ipc_version; @@ -391,6 +451,10 @@ struct io_device { /* Rx queue of sk_buff */ struct sk_buff_head sk_rx_q; + /* RX state used in RX FSM */ + enum iod_rx_state curr_rx_state; + enum iod_rx_state next_rx_state; + /* ** work for each io device, when delayed work needed ** use this for private io device rx action @@ -447,6 +511,9 @@ struct link_device { /* SIPC version */ enum sipc_ver ipc_version; + /* Maximum IPC device = the last IPC device (e.g. IPC_RFS) + 1 */ + int max_ipc_dev; + /* Modem data */ struct modem_data *mdm_data; @@ -459,6 +526,12 @@ struct link_device { /* Operation mode of the link device */ enum link_mode mode; + /* completion for waiting for link initialization */ + struct completion init_cmpl; + + /* completion for waiting for PIF initialization in a CP */ + struct completion pif_cmpl; + struct io_device *fmt_iods[4]; /* TX queue of socket buffers */ @@ -468,13 +541,30 @@ struct link_device { struct sk_buff_head *skb_txq[MAX_IPC_DEV]; + /* RX queue of socket buffers */ + struct sk_buff_head sk_fmt_rx_q; + struct sk_buff_head sk_raw_rx_q; + struct sk_buff_head sk_rfs_rx_q; + + struct sk_buff_head *skb_rxq[MAX_IPC_DEV]; + bool raw_tx_suspended; /* for misc dev */ struct completion raw_tx_resumed_by_cp; + /** + * This flag is for TX flow control on network interface. + * This must be set and clear only by a flow control command from CP. + */ + bool suspend_netif_tx; + struct workqueue_struct *tx_wq; struct work_struct tx_work; struct delayed_work tx_delayed_work; - struct delayed_work tx_dwork; + + struct delayed_work *tx_dwork[MAX_IPC_DEV]; + struct delayed_work fmt_tx_dwork; + struct delayed_work raw_tx_dwork; + struct delayed_work rfs_tx_dwork; struct workqueue_struct *rx_wq; struct work_struct rx_work; @@ -495,20 +585,26 @@ struct link_device { int (*send)(struct link_device *ld, struct io_device *iod, struct sk_buff *skb); - int (*udl_start)(struct link_device *ld, struct io_device *iod); - - int (*force_dump)(struct link_device *ld, struct io_device *iod); - - int (*dump_start)(struct link_device *ld, struct io_device *iod); + /* method for CP booting */ + int (*xmit_boot)(struct link_device *ld, struct io_device *iod, + unsigned long arg); - int (*modem_update)(struct link_device *ld, struct io_device *iod, + /* methods for CP firmware upgrade */ + int (*dload_start)(struct link_device *ld, struct io_device *iod); + int (*firm_update)(struct link_device *ld, struct io_device *iod, unsigned long arg); + /* methods for CP crash dump */ + int (*force_dump)(struct link_device *ld, struct io_device *iod); + int (*dump_start)(struct link_device *ld, struct io_device *iod); int (*dump_update)(struct link_device *ld, struct io_device *iod, unsigned long arg); + int (*dump_finish)(struct link_device *ld, struct io_device *iod, + unsigned long arg); + /* IOCTL extension */ int (*ioctl)(struct link_device *ld, struct io_device *iod, - unsigned cmd, unsigned long _arg); + unsigned cmd, unsigned long arg); }; /** rx_alloc_skb - allocate an skbuff and set skb's iod, ld @@ -567,6 +663,9 @@ struct modem_shared { struct mif_storage storage; spinlock_t lock; + /* CP crash information */ + char cp_crash_info[530]; + /* loopbacked IP address * default is 0.0.0.0 (disabled) * after you setted this, you can use IP packet loopback using this IP. @@ -586,35 +685,52 @@ struct modem_ctl { struct sim_state sim_state; unsigned gpio_cp_on; + unsigned gpio_cp_off; unsigned gpio_reset_req_n; unsigned gpio_cp_reset; + + /* for broadcasting AP's PM state (active or sleep) */ unsigned gpio_pda_active; + + /* for checking aliveness of CP */ unsigned gpio_phone_active; + int irq_phone_active; + + /* for AP-CP power management (PM) handshaking */ + unsigned gpio_ap_wakeup; + int irq_ap_wakeup; + unsigned gpio_ap_status; + unsigned gpio_cp_wakeup; + unsigned gpio_cp_status; + int irq_cp_status; + + /* for USB/HSIC PM */ + unsigned gpio_host_wakeup; + int irq_host_wakeup; + unsigned gpio_host_active; + unsigned gpio_slave_wakeup; + +#ifdef CONFIG_EXYNOS4_CPUFREQ + /* cpu/bus frequency lock */ + unsigned gpio_cpufreq_lock; + struct delayed_work work_cpu_lock; + struct delayed_work work_cpu_unlock; +#endif + unsigned gpio_cp_dump_int; unsigned gpio_ap_dump_int; unsigned gpio_flm_uart_sel; + unsigned gpio_cp_warm_reset; #if defined(CONFIG_MACH_M0_CTC) unsigned gpio_flm_uart_sel_rev06; #endif - unsigned gpio_cp_warm_reset; - unsigned gpio_cp_off; - unsigned gpio_sim_detect; - unsigned gpio_dynamic_switching; - int irq_phone_active; + unsigned gpio_sim_detect; int irq_sim_detect; -#ifdef CONFIG_LTE_MODEM_CMC221 - const struct attribute_group *group; - unsigned gpio_slave_wakeup; - unsigned gpio_host_wakeup; - unsigned gpio_host_active; - int irq_host_wakeup; - - struct delayed_work dwork; -#endif /*CONFIG_LTE_MODEM_CMC221*/ - - struct work_struct work; +#ifdef CONFIG_LINK_DEVICE_PLD + unsigned gpio_fpga_cs_n; +#endif #if defined(CONFIG_MACH_U1_KOR_LGT) unsigned gpio_cp_reset_msm; @@ -635,9 +751,13 @@ struct modem_ctl { unsigned gpio_cp_ctrl2; #endif -#ifdef CONFIG_LINK_DEVICE_PLD - unsigned gpio_fpga_cs_n; -#endif + /* Switch with 2 links in a modem */ + unsigned gpio_link_switch; + + const struct attribute_group *group; + + struct delayed_work dwork; + struct work_struct work; struct modemctl_ops ops; struct io_device *iod; @@ -651,119 +771,45 @@ struct modem_ctl { bool need_switch_to_usb; bool sim_polarity; + + bool sim_shutdown_req; + void (*modem_complete)(struct modem_ctl *mc); }; int sipc4_init_io_device(struct io_device *iod); int sipc5_init_io_device(struct io_device *iod); -/** - * 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; -} - -/** - * 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; - } -} - -/** - * 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); -} - -/** - * 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; -} - -extern void set_sromc_access(bool access); +bool sipc5_start_valid(u8 *frm); +bool sipc5_padding_exist(u8 *frm); +bool sipc5_multi_frame(u8 *frm); +bool sipc5_ext_len(u8 *frm); +int sipc5_get_hdr_len(u8 *frm); +u8 sipc5_get_ch_id(u8 *frm); +u8 sipc5_get_ctrl_field(u8 *frm); +int sipc5_get_frame_len(u8 *frm); +int sipc5_calc_padding_size(int len); +int sipc5_get_total_len(u8 *frm); + +u8 sipc5_build_config(struct io_device *iod, struct link_device *ld, u32 count); +void sipc5_build_header(struct io_device *iod, struct link_device *ld, + u8 *buff, u8 cfg, u8 ctrl, u32 count); #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; +#endif - int irq; +#define STD_UDL_STEP_MASK 0x0000000F +#define STD_UDL_SEND 0x1 +#define STD_UDL_CRC 0xC - struct completion comp; - atomic_t ref_sem; - unsigned long flags; +struct std_dload_info { + u32 size; + u32 mtu; + u32 num_frames; +} __packed; - const struct attribute_group *group; -}; -#endif +u32 std_udl_get_cmd(u8 *frm); +bool std_udl_with_payload(u32 cmd); #endif diff --git a/drivers/misc/modem_if/modem_sim_slot_switch.c b/drivers/misc/modem_if/modem_sim_slot_switch.c index c8e83ed..366d0fa 100644 --- a/drivers/misc/modem_if/modem_sim_slot_switch.c +++ b/drivers/misc/modem_if/modem_sim_slot_switch.c @@ -17,7 +17,7 @@ static ssize_t get_slot_switch(struct device *dev, struct device_attribute *attr //return '0' slot path is '||', return '1' slot path is 'X' value = gpio_get_value(GPIO_UIM_SIM_SEL); #if defined(CONFIG_MACH_T0_CHN_CTC) - if (system_rev < 7) + if (system_rev <= 7) value = (~value & 0x1); #endif printk("Current Slot is %x\n", value); @@ -34,7 +34,7 @@ static ssize_t set_slot_switch(struct device *dev, struct device_attribute *attr switch(value) { case 0: #if defined(CONFIG_MACH_T0_CHN_CTC) - if (system_rev < 7) + if (system_rev <= 7) gpio_set_value(GPIO_UIM_SIM_SEL, 1); else #endif @@ -43,7 +43,7 @@ static ssize_t set_slot_switch(struct device *dev, struct device_attribute *attr break; case 1: #if defined(CONFIG_MACH_T0_CHN_CTC) - if (system_rev < 7) + if (system_rev <= 7) gpio_set_value(GPIO_UIM_SIM_SEL, 0); else #endif @@ -57,7 +57,8 @@ static ssize_t set_slot_switch(struct device *dev, struct device_attribute *attr return size; } -static DEVICE_ATTR(slot_sel, S_IRUGO |S_IWUGO | S_IRUSR | S_IWUSR, get_slot_switch, set_slot_switch); +static DEVICE_ATTR(slot_sel, S_IRUGO | S_IWUSR | S_IWGRP, + get_slot_switch, set_slot_switch); static int __init slot_switch_manager_init(void) { @@ -75,7 +76,7 @@ static int __init slot_switch_manager_init(void) 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) - if (system_rev < 7) + if (system_rev <= 7) gpio_set_value(GPIO_UIM_SIM_SEL, 1); else #endif diff --git a/drivers/misc/modem_if/modem_utils.c b/drivers/misc/modem_if/modem_utils.c index d62aaa6..e829324 100644 --- a/drivers/misc/modem_if/modem_utils.c +++ b/drivers/misc/modem_if/modem_utils.c @@ -12,18 +12,35 @@ * */ -#include <linux/netdevice.h> -#include <linux/platform_data/modem.h> +#include <linux/init.h> +#include <linux/module.h> #include <linux/platform_device.h> +#include <linux/interrupt.h> +#include <linux/miscdevice.h> +#include <linux/netdevice.h> #include <linux/skbuff.h> #include <linux/ip.h> +#include <net/ip.h> #include <linux/tcp.h> #include <linux/udp.h> #include <linux/rtc.h> #include <linux/time.h> -#include <net/ip.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 "modem.h" #include "modem_prj.h" +#include "modem_variation.h" #include "modem_utils.h" #define CMD_SUSPEND ((unsigned short)(0x00CA)) @@ -35,42 +52,29 @@ "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; +static const char *hex = "0123456789abcdef"; - 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); - } +void ts2utc(struct timespec *ts, struct utc_time *utc) +{ + struct tm tm; + + time_to_tm((ts->tv_sec - (sys_tz.tz_minuteswest * 60)), 0, &tm); + utc->year = 1900 + tm.tm_year; + utc->mon = 1 + tm.tm_mon; + utc->day = tm.tm_mday; + utc->hour = tm.tm_hour; + utc->min = tm.tm_min; + utc->sec = tm.tm_sec; + utc->msec = ns2ms(ts->tv_nsec); +} - 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; +void get_utc_time(struct utc_time *utc) +{ + struct timespec ts; + getnstimeofday(&ts); + ts2utc(&ts, utc); } -#endif +EXPORT_SYMBOL(get_utc_time); int mif_dump_log(struct modem_shared *msd, struct io_device *iod) { @@ -82,7 +86,7 @@ int mif_dump_log(struct modem_shared *msd, struct io_device *iod) 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__); + mif_err("ERR! alloc_skb fail\n"); spin_unlock_irqrestore(&msd->lock, flags); return -ENOMEM; } @@ -164,7 +168,6 @@ void _mif_com_log(enum mif_log_id id, struct mif_common_block *block; unsigned long int flags; va_list args; - int ret; spin_lock_irqsave(&msd->lock, flags); @@ -179,7 +182,7 @@ void _mif_com_log(enum mif_log_id id, block->time = get_kernel_time(); va_start(args, format); - ret = vsnprintf(block->buff, MAX_COM_LOG_SIZE, format, args); + vsnprintf(block->buff, MAX_COM_LOG_SIZE, format, args); va_end(args); } @@ -209,13 +212,12 @@ void _mif_time_log(enum mif_log_id id, struct modem_shared *msd, /* dump2hex * dump data to hex as fast as possible. - * the length of @buf must be greater than "@len * 3" + * the length of @buff must be greater than "@len * 3" * it need 3 bytes per one data byte to print. */ -static inline int dump2hex(char *buf, const char *data, size_t len) +static inline int dump2hex(char *buff, const char *data, size_t len) { - static const char *hex = "0123456789abcdef"; - char *dest = buf; + char *dest = buff; int i; for (i = 0; i < len; i++) { @@ -228,24 +230,26 @@ static inline int dump2hex(char *buf, const char *data, size_t len) *dest = '\0'; - return dest - buf; + return dest - buff; } -int pr_ipc(const char *str, const char *data, size_t len) +void pr_ipc(int level, const char *tag, const char *data, size_t len) { - struct timeval tv; - struct tm date; - unsigned char hexstr[128]; + struct utc_time utc; + unsigned char str[128]; - do_gettimeofday(&tv); - time_to_tm((tv.tv_sec - sys_tz.tz_minuteswest * 60), 0, &date); - - dump2hex(hexstr, data, (len > 40 ? 40 : len)); + if (level < 0) + return; - 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); + get_utc_time(&utc); + dump2hex(str, data, (len > 32 ? 32 : len)); + if (level > 0) { + pr_err("%s: [%02d:%02d:%02d.%03d] %s: %s\n", MIF_TAG, + utc.hour, utc.min, utc.sec, utc.msec, tag, str); + } else { + pr_info("%s: [%02d:%02d:%02d.%03d] %s: %s\n", MIF_TAG, + utc.hour, utc.min, utc.sec, utc.msec, tag, str); + } } /* print buffer as hex string */ @@ -253,12 +257,12 @@ int pr_buffer(const char *tag, const char *data, size_t data_len, size_t max_len) { size_t len = min(data_len, max_len); - unsigned char hexstr[len ? len * 3 : 1]; /* 1 <= sizeof <= max_len*3 */ - dump2hex(hexstr, data, len); + unsigned char str[len ? len * 3 : 1]; /* 1 <= sizeof <= max_len*3 */ + dump2hex(str, data, len); /* don't change this printk to mif_debug for print this as level7 */ - return printk(KERN_INFO "mif: %s(%u): %s%s\n", tag, data_len, hexstr, - len == data_len ? "" : " ..."); + return printk(KERN_INFO "%s: %s(%u): %s%s\n", MIF_TAG, tag, data_len, + str, (len == data_len) ? "" : " ..."); } /* flow control CM from CP, it use in serial devices */ @@ -285,7 +289,7 @@ int link_rx_flowctl_cmd(struct link_device *ld, const char *data, size_t len) break; default: - mif_err("flowctl BACMD: %04X\n", *cmd); + mif_err("flowctl BAD CMD: %04X\n", *cmd); break; } } @@ -411,6 +415,42 @@ void rawdevs_set_tx_link(struct modem_shared *msd, enum modem_link link_type) iodevs_for_each(msd, iodev_set_tx_link, ld); } +void mif_netif_stop(struct link_device *ld) +{ + struct io_device *iod; + + if (ld->ipc_version < SIPC_VER_50) + iod = link_get_iod_with_channel(ld, 0x20 | RMNET0_CH_ID); + else + iod = link_get_iod_with_channel(ld, RMNET0_CH_ID); + + if (iod) + iodevs_for_each(iod->msd, iodev_netif_stop, 0); +} + +void mif_netif_wake(struct link_device *ld) +{ + struct io_device *iod; + + /** + * If ld->suspend_netif_tx is true, this means that there was a SUSPEND + * flow control command from CP so MIF must wait for a RESUME command + * from CP. + */ + if (ld->suspend_netif_tx) { + mif_info("%s: waiting for FLOW_CTRL_RESUME\n", ld->name); + return; + } + + if (ld->ipc_version < SIPC_VER_50) + iod = link_get_iod_with_channel(ld, 0x20 | RMNET0_CH_ID); + else + iod = link_get_iod_with_channel(ld, RMNET0_CH_ID); + + if (iod) + iodevs_for_each(iod->msd, iodev_netif_wake, 0); +} + /** * ipv4str_to_be32 - ipv4 string to be32 (big endian 32bits integer) * @return: return zero when errors occurred @@ -447,7 +487,7 @@ void mif_add_timer(struct timer_list *timer, unsigned long expire, add_timer(timer); } -void mif_print_data(char *buf, int len) +void mif_print_data(const char *buff, int len) { int words = len >> 4; int residue = len - (words << 4); @@ -458,11 +498,8 @@ void mif_print_data(char *buf, int len) /* Make the last line, if ((len % 16) > 0) */ if (residue > 0) { - memset(last, 0, sizeof(last)); - memset(tb, 0, sizeof(tb)); - b = buf + (words << 4); - sprintf(last, "%04X: ", (words << 4)); + b = (char *)buff + (words << 4); for (i = 0; i < residue; i++) { sprintf(tb, "%02x ", b[i]); strcat(last, tb); @@ -474,7 +511,7 @@ void mif_print_data(char *buf, int len) } for (i = 0; i < words; i++) { - b = buf + (i << 4); + b = (char *)buff + (i << 4); mif_err("%04X: " "%02x %02x %02x %02x %02x %02x %02x %02x " "%02x %02x %02x %02x %02x %02x %02x %02x\n", @@ -488,13 +525,141 @@ void mif_print_data(char *buf, int len) mif_err("%s\n", last); } +void mif_dump2format16(const char *data, int len, char *buff, char *tag) +{ + char *d; + int i; + int words = len >> 4; + int residue = len - (words << 4); + char line[LINE_BUFF_SIZE]; + char tb[8]; + + for (i = 0; i < words; i++) { + memset(line, 0, LINE_BUFF_SIZE); + d = (char *)data + (i << 4); + + if (tag) + sprintf(line, "%s%04X| " + "%02x %02x %02x %02x " + "%02x %02x %02x %02x " + "%02x %02x %02x %02x " + "%02x %02x %02x %02x\n", + tag, (i << 4), + d[0], d[1], d[2], d[3], + d[4], d[5], d[6], d[7], + d[8], d[9], d[10], d[11], + d[12], d[13], d[14], d[15]); + else + sprintf(line, "%04X| " + "%02x %02x %02x %02x " + "%02x %02x %02x %02x " + "%02x %02x %02x %02x " + "%02x %02x %02x %02x\n", + (i << 4), + d[0], d[1], d[2], d[3], + d[4], d[5], d[6], d[7], + d[8], d[9], d[10], d[11], + d[12], d[13], d[14], d[15]); + + strcat(buff, line); + } + + /* Make the last line, if (len % 16) > 0 */ + if (residue > 0) { + memset(line, 0, LINE_BUFF_SIZE); + memset(tb, 0, sizeof(tb)); + d = (char *)data + (words << 4); + + if (tag) + sprintf(line, "%s%04X|", tag, (words << 4)); + else + sprintf(line, "%04X|", (words << 4)); + + for (i = 0; i < residue; i++) { + sprintf(tb, " %02x", d[i]); + strcat(line, tb); + if ((i & 0x3) == 0x3) { + sprintf(tb, " "); + strcat(line, tb); + } + } + strcat(line, "\n"); + + strcat(buff, line); + } +} + +void mif_dump2format4(const char *data, int len, char *buff, char *tag) +{ + char *d; + int i; + int words = len >> 2; + int residue = len - (words << 2); + char line[LINE_BUFF_SIZE]; + char tb[8]; + + for (i = 0; i < words; i++) { + memset(line, 0, LINE_BUFF_SIZE); + d = (char *)data + (i << 2); + + if (tag) + sprintf(line, "%s%04X| %02x %02x %02x %02x\n", + tag, (i << 2), d[0], d[1], d[2], d[3]); + else + sprintf(line, "%04X| %02x %02x %02x %02x\n", + (i << 2), d[0], d[1], d[2], d[3]); + + strcat(buff, line); + } + + /* Make the last line, if (len % 4) > 0 */ + if (residue > 0) { + memset(line, 0, LINE_BUFF_SIZE); + memset(tb, 0, sizeof(tb)); + d = (char *)data + (words << 2); + + if (tag) + sprintf(line, "%s%04X|", tag, (words << 2)); + else + sprintf(line, "%04X|", (words << 2)); + + for (i = 0; i < residue; i++) { + sprintf(tb, " %02x", d[i]); + strcat(line, tb); + } + strcat(line, "\n"); + + strcat(buff, line); + } +} + +void mif_print_dump(const char *data, int len, int width) +{ + char *buff; + + buff = kzalloc(len << 3, GFP_ATOMIC); + if (!buff) { + mif_err("ERR! kzalloc fail\n"); + return; + } + + if (width == 16) + mif_dump2format16(data, len, buff, LOG_TAG); + else + mif_dump2format4(data, len, buff, LOG_TAG); + + pr_info("%s", buff); + + kfree(buff); +} + void print_sipc4_hdlc_fmt_frame(const u8 *psrc) { u8 *frm; /* HDLC Frame */ struct fmt_hdr *hh; /* HDLC Header */ struct sipc_fmt_hdr *fh; /* IPC Header */ - u16 hh_len = sizeof(struct fmt_hdr); - u16 fh_len = sizeof(struct sipc_fmt_hdr); + int hh_len = sizeof(struct fmt_hdr); + int fh_len = sizeof(struct sipc_fmt_hdr); u8 *data; int dlen; @@ -531,7 +696,7 @@ void print_sipc4_hdlc_fmt_frame(const u8 *psrc) void print_sipc4_fmt_frame(const u8 *psrc) { struct sipc_fmt_hdr *fh = (struct sipc_fmt_hdr *)psrc; - u16 fh_len = sizeof(struct sipc_fmt_hdr); + int fh_len = sizeof(struct sipc_fmt_hdr); u8 *data; int dlen; @@ -558,8 +723,8 @@ void print_sipc5_link_fmt_frame(const u8 *psrc) u8 *lf; /* Link Frame */ struct sipc5_link_hdr *lh; /* Link Header */ struct sipc_fmt_hdr *fh; /* IPC Header */ - u16 lh_len; - u16 fh_len; + int lh_len; + int fh_len; u8 *data; int dlen; @@ -567,10 +732,7 @@ void print_sipc5_link_fmt_frame(const u8 *psrc) /* Point HDLC header and IPC header */ lh = (struct sipc5_link_hdr *)lf; - if (lh->cfg & SIPC5_CTL_FIELD_EXIST) - lh_len = SIPC5_HEADER_SIZE_WITH_CTL_FLD; - else - lh_len = SIPC5_MIN_HEADER_SIZE; + lh_len = (u16)sipc5_get_hdr_len((u8 *)lh); fh = (struct sipc_fmt_hdr *)(lf + lh_len); fh_len = sizeof(struct sipc_fmt_hdr); @@ -601,8 +763,8 @@ static void strcat_tcp_header(char *buff, u8 *pkt) { struct tcphdr *tcph = (struct tcphdr *)pkt; int eol; - char line[LINE_BUFF_SIZE]; - char flag_str[32]; + char line[LINE_BUFF_SIZE] = {0, }; + char flag_str[32] = {0, }; /*------------------------------------------------------------------------- @@ -630,21 +792,17 @@ static void strcat_tcp_header(char *buff, u8 *pkt) -------------------------------------------------------------------------*/ - 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)); + "%s: TCP:: Src.Port %u, Dst.Port %u\n", + MIF_TAG, 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), + "%s: TCP:: SEQ 0x%08X(%u), ACK 0x%08X(%u)\n", + MIF_TAG, 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(flag_str, "CWR "); if (tcph->ece) @@ -664,12 +822,12 @@ static void strcat_tcp_header(char *buff, u8 *pkt) eol = strlen(flag_str) - 1; if (eol > 0) flag_str[eol] = 0; - snprintf(line, LINE_BUFF_SIZE, "mif: TCP:: Flags {%s}\n", flag_str); + snprintf(line, LINE_BUFF_SIZE, "%s: TCP:: Flags {%s}\n", + MIF_TAG, 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", + "%s: TCP:: Window %u, Checksum 0x%04X, Urgent %u\n", MIF_TAG, ntohs(tcph->window), ntohs(tcph->check), ntohs(tcph->urg_ptr)); strcat(buff, line); } @@ -677,7 +835,7 @@ static void strcat_tcp_header(char *buff, u8 *pkt) static void strcat_udp_header(char *buff, u8 *pkt) { struct udphdr *udph = (struct udphdr *)pkt; - char line[LINE_BUFF_SIZE]; + char line[LINE_BUFF_SIZE] = {0, }; /*------------------------------------------------------------------------- @@ -693,41 +851,39 @@ static void strcat_udp_header(char *buff, u8 *pkt) -------------------------------------------------------------------------*/ - 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)); + "%s: UDP:: Src.Port %u, Dst.Port %u\n", + MIF_TAG, ntohs(udph->source), ntohs(udph->dest)); 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)); + "%s: UDP:: Length %u, Checksum 0x%04X\n", + MIF_TAG, ntohs(udph->len), ntohs(udph->check)); strcat(buff, line); if (ntohs(udph->dest) == 53) { - memset(line, 0, LINE_BUFF_SIZE); - snprintf(line, LINE_BUFF_SIZE, "mif: UDP:: DNS query!!!\n"); + snprintf(line, LINE_BUFF_SIZE, "%s: UDP:: DNS query!!!\n", + MIF_TAG); strcat(buff, line); } if (ntohs(udph->source) == 53) { - memset(line, 0, LINE_BUFF_SIZE); - snprintf(line, LINE_BUFF_SIZE, "mif: UDP:: DNS response!!!\n"); + snprintf(line, LINE_BUFF_SIZE, "%s: UDP:: DNS response!!!\n", + MIF_TAG); strcat(buff, line); } } -void print_ip4_packet(u8 *ip_pkt, bool tx) +void print_ip4_packet(const u8 *ip_pkt, bool tx) { char *buff; struct iphdr *iph = (struct iphdr *)ip_pkt; - u8 *pkt = ip_pkt + (iph->ihl << 2); + u8 *pkt = (u8 *)ip_pkt + (iph->ihl << 2); u16 flags = (ntohs(iph->frag_off) & 0xE000); u16 frag_off = (ntohs(iph->frag_off) & 0x1FFF); int eol; - char line[LINE_BUFF_SIZE]; - char flag_str[16]; + char line[LINE_BUFF_SIZE] = {0, }; + char flag_str[16] = {0, }; /*--------------------------------------------------------------------------- IPv4 Header Format @@ -764,32 +920,25 @@ void print_ip4_packet(u8 *ip_pkt, bool tx) 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)); + "%s: IP4:: Version %u, Header Length %u, TOS %u, Length %u\n", + MIF_TAG, iph->version, (iph->ihl << 2), iph->tos, + ntohs(iph->tot_len)); 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); + snprintf(line, LINE_BUFF_SIZE, "%s: IP4:: ID %u, Fragment Offset %u\n", + MIF_TAG, ntohs(iph->id), frag_off); 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) @@ -799,19 +948,18 @@ void print_ip4_packet(u8 *ip_pkt, bool tx) eol = strlen(flag_str) - 1; if (eol > 0) flag_str[eol] = 0; - snprintf(line, LINE_BUFF_SIZE, "mif: IP4:: Flags {%s}\n", flag_str); + snprintf(line, LINE_BUFF_SIZE, "%s: IP4:: Flags {%s}\n", + MIF_TAG, 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)); + "%s: IP4:: TTL %u, Protocol %u, Header Checksum 0x%04X\n", + MIF_TAG, iph->ttl, iph->protocol, ntohs(iph->check)); 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], + "%s: IP4:: Src.IP %u.%u.%u.%u, Dst.IP %u.%u.%u.%u\n", + MIF_TAG, 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); @@ -828,7 +976,6 @@ void print_ip4_packet(u8 *ip_pkt, bool tx) break; } - memset(line, 0, LINE_BUFF_SIZE); snprintf(line, LINE_BUFF_SIZE, "%s\n", LINE_SEPARATOR); strcat(buff, line); @@ -837,7 +984,7 @@ void print_ip4_packet(u8 *ip_pkt, bool tx) kfree(buff); } -bool is_dns_packet(u8 *ip_pkt) +bool is_dns_packet(const u8 *ip_pkt) { struct iphdr *iph = (struct iphdr *)ip_pkt; struct udphdr *udph = (struct udphdr *)(ip_pkt + (iph->ihl << 2)); @@ -852,7 +999,7 @@ bool is_dns_packet(u8 *ip_pkt) return false; } -bool is_syn_packet(u8 *ip_pkt) +bool is_syn_packet(const u8 *ip_pkt) { struct iphdr *iph = (struct iphdr *)ip_pkt; struct tcphdr *tcph = (struct tcphdr *)(ip_pkt + (iph->ihl << 2)); @@ -867,124 +1014,248 @@ bool is_syn_packet(u8 *ip_pkt) return false; } -int memcmp16_to_io(const void __iomem *to, void *from, int size) +/** + * mif_register_isr + * @irq: IRQ number for a DPRAM interrupt + * @isr: function pointer to an interrupt service routine + * @flags: set of interrupt flags + * @name: name of the interrupt + * @data: pointer to a data for @isr + * + * Registers the ISR for the IRQ number. + */ +int mif_register_isr(unsigned int irq, irq_handler_t isr, unsigned long flags, + const char *name, void *data) { - 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++; + int ret; + + ret = request_irq(irq, isr, flags, name, data); + if (ret) { + mif_err("%s: ERR! request_irq fail (err %d)\n", name, ret); + return ret; } - return diff; + ret = enable_irq_wake(irq); + if (ret) + mif_err("%s: ERR! enable_irq_wake fail (err %d)\n", name, ret); + + mif_err("%s (#%d) handler registered\n", name, irq); + + return 0; } -int mif_test_dpram(char *dp_name, u8 __iomem *start, u32 size) +int mif_test_dpram(char *dp_name, void __iomem *start, u16 bytes) { - u8 __iomem *dst; - int i; + u16 i; + u16 words = bytes >> 1; + u16 __iomem *dst = (u16 __iomem *)start; u16 val; + int err_cnt = 0; - mif_info("%s: start = 0x%p, size = %d\n", dp_name, start, size); + mif_err("%s: start 0x%p, bytes %d\n", dp_name, start, bytes); - dst = start; - for (i = 0; i < (size >> 1); i++) { - iowrite16((i & 0xFFFF), dst); - dst += 2; + mif_err("%s: 0/6 stage ...\n", dp_name); + for (i = 1; i <= 100; i++) { + iowrite16(0x1234, dst); + val = ioread16(dst); + if (val != 0x1234) { + mif_err("%s: [0x0000] read 0x%04X != written 0x1234 " + "(try# %d)\n", dp_name, val, i); + err_cnt++; + } + } + + if (err_cnt > 0) { + mif_err("%s: FAIL!!!\n", dp_name); + return -EINVAL; + } + + mif_err("%s: 1/6 stage ...\n", dp_name); + dst = (u16 __iomem *)start; + for (i = 0; i < words; i++) { + iowrite16(0, dst); + dst++; } - dst = start; - for (i = 0; i < (size >> 1); i++) { + dst = (u16 __iomem *)start; + for (i = 0; i < words; 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; + if (val != 0x0000) { + mif_err("%s: ERR! [0x%04X] read 0x%04X != written " + "0x0000\n", dp_name, i, val); + err_cnt++; } - dst += 2; + dst++; + } + + if (err_cnt > 0) { + mif_err("%s: FAIL!!!\n", dp_name); + return -EINVAL; } - dst = start; - for (i = 0; i < (size >> 1); i++) { + mif_err("%s: 2/6 stage ...\n", dp_name); + dst = (u16 __iomem *)start; + for (i = 0; i < words; i++) { + iowrite16(0xFFFF, dst); + dst++; + } + + dst = (u16 __iomem *)start; + for (i = 0; i < words; i++) { + val = ioread16(dst); + if (val != 0xFFFF) { + mif_err("%s: ERR! [0x%04X] read 0x%04X != written " + "0xFFFF\n", dp_name, i, val); + err_cnt++; + } + dst++; + } + + if (err_cnt > 0) { + mif_err("%s: FAIL!!!\n", dp_name); + return -EINVAL; + } + + mif_err("%s: 3/6 stage ...\n", dp_name); + dst = (u16 __iomem *)start; + for (i = 0; i < words; i++) { iowrite16(0x00FF, dst); - dst += 2; + dst++; } - dst = start; - for (i = 0; i < (size >> 1); i++) { + dst = (u16 __iomem *)start; + for (i = 0; i < words; i++) { val = ioread16(dst); if (val != 0x00FF) { - mif_info("%s: ERR! dst[%d] 0x%04X != 0x00FF\n", - dp_name, i, val); - return -EINVAL; + mif_err("%s: ERR! [0x%04X] read 0x%04X != written " + "0x00FF\n", dp_name, i, val); + err_cnt++; } - dst += 2; + dst++; } - dst = start; - for (i = 0; i < (size >> 1); i++) { + if (err_cnt > 0) { + mif_err("%s: FAIL!!!\n", dp_name); + return -EINVAL; + } + + mif_err("%s: 4/6 stage ...\n", dp_name); + dst = (u16 __iomem *)start; + for (i = 0; i < words; i++) { iowrite16(0x0FF0, dst); - dst += 2; + dst++; } - dst = start; - for (i = 0; i < (size >> 1); i++) { + dst = (u16 __iomem *)start; + for (i = 0; i < words; i++) { val = ioread16(dst); if (val != 0x0FF0) { - mif_info("%s: ERR! dst[%d] 0x%04X != 0x0FF0\n", - dp_name, i, val); - return -EINVAL; + mif_err("%s: ERR! [0x%04X] read 0x%04X != written " + "0x0FF0\n", dp_name, i, val); + err_cnt++; } - dst += 2; + dst++; + } + + if (err_cnt > 0) { + mif_err("%s: FAIL!!!\n", dp_name); + return -EINVAL; } - dst = start; - for (i = 0; i < (size >> 1); i++) { + mif_err("%s: 5/6 stage ...\n", dp_name); + dst = (u16 __iomem *)start; + for (i = 0; i < words; i++) { iowrite16(0xFF00, dst); - dst += 2; + dst++; } - dst = start; - for (i = 0; i < (size >> 1); i++) { + dst = (u16 __iomem *)start; + for (i = 0; i < words; i++) { val = ioread16(dst); if (val != 0xFF00) { - mif_info("%s: ERR! dst[%d] 0x%04X != 0xFF00\n", - dp_name, i, val); - return -EINVAL; + mif_err("%s: ERR! [0x%04X] read 0x%04X != written " + "0xFF00\n", dp_name, i, val); + err_cnt++; } - dst += 2; + dst++; } - dst = start; - for (i = 0; i < (size >> 1); i++) { - iowrite16(0, dst); - dst += 2; + mif_err("%s: 6/6 stage ...\n", dp_name); + dst = (u16 __iomem *)start; + for (i = 0; i < words; i++) { + iowrite16((i & 0xFFFF), dst); + dst++; } - dst = start; - for (i = 0; i < (size >> 1); i++) { + dst = (u16 __iomem *)start; + for (i = 0; i < words; i++) { val = ioread16(dst); - if (val != 0) { - mif_info("%s: ERR! dst[%d] 0x%04X != 0\n", - dp_name, i, val); - return -EINVAL; + if (val != (i & 0xFFFF)) { + mif_err("%s: ERR! [0x%04X] read 0x%04X != written " + "0x%04X\n", dp_name, i, val, (i & 0xFFFF)); + err_cnt++; } - dst += 2; + dst++; + } + + if (err_cnt > 0) { + mif_err("%s: FAIL!!!\n", dp_name); + return -EINVAL; + } + + mif_err("%s: PASS!!!\n", dp_name); + + dst = (u16 __iomem *)start; + for (i = 0; i < words; i++) { + iowrite16(0, dst); + dst++; } - mif_info("%s: PASS!!!\n", dp_name); return 0; } +struct file *mif_open_file(const char *path) +{ + struct file *fp; + mm_segment_t old_fs; + + old_fs = get_fs(); + set_fs(get_ds()); + + fp = filp_open(path, O_RDWR|O_CREAT|O_APPEND, 0666); + + set_fs(old_fs); + + if (IS_ERR(fp)) + return NULL; + + return fp; +} + +void mif_save_file(struct file *fp, const char *buff, size_t size) +{ + int ret; + mm_segment_t old_fs; + + old_fs = get_fs(); + set_fs(get_ds()); + + ret = fp->f_op->write(fp, buff, size, &fp->f_pos); + if (ret < 0) + mif_err("ERR! write fail\n"); + + set_fs(old_fs); +} + +void mif_close_file(struct file *fp) +{ + mm_segment_t old_fs; + + old_fs = get_fs(); + set_fs(get_ds()); + + filp_close(fp, NULL); + + set_fs(old_fs); +} + diff --git a/drivers/misc/modem_if/modem_utils.h b/drivers/misc/modem_if/modem_utils.h index 7225ec2..15edb43 100644 --- a/drivers/misc/modem_if/modem_utils.h +++ b/drivers/misc/modem_if/modem_utils.h @@ -16,6 +16,7 @@ #define __MODEM_UTILS_H__ #include <linux/rbtree.h> +#include "modem_prj.h" #define IS_CONNECTED(iod, ld) ((iod)->link_types & LINKTYPE((ld)->link_type)) @@ -26,6 +27,8 @@ #define MAX_IPC_SKB_SIZE 4096 #define MAX_LOG_SIZE 64 +#define MIF_TAG "mif" + #define MAX_LOG_CNT (MAX_MIF_BUFF_SIZE / MAX_LOG_SIZE) #define MIF_ID_SIZE sizeof(enum mif_log_id) @@ -96,7 +99,19 @@ struct mif_time_block { char buff[MAX_TIM_LOG_SIZE]; }; -int mif_dump_dpram(struct io_device *); +static inline int ns2us(long ns) +{ + return (ns > 0) ? (ns / 1000) : 0; +} + +static inline int ns2ms(long ns) +{ + return (ns > 0) ? (ns / 1000000) : 0; +} + +void ts2utc(struct timespec *ts, struct utc_time *utc); +void get_utc_time(struct utc_time *utc); + int mif_dump_log(struct modem_shared *, struct io_device *); #define mif_irq_log(msd, map, data, len) \ @@ -141,7 +156,7 @@ static inline unsigned int countbits(unsigned int n) } /* print IPC message as hex string with UTC time */ -int pr_ipc(const char *str, const char *data, size_t len); +void pr_ipc(int level, const char *tag, const char *data, size_t len); /* print buffer as hex string */ int pr_buffer(const char *tag, const char *data, size_t data_len, @@ -156,10 +171,13 @@ int pr_buffer(const char *tag, const char *data, size_t data_len, pr_buffer(tag, (char *)((urb)->transfer_buffer), \ (size_t)((urb)->actual_length), (size_t)16) +/* Stop/wake all TX queues in network interfaces */ +void mif_netif_stop(struct link_device *ld); +void mif_netif_wake(struct link_device *ld); + /* flow control CMD from CP, it use in serial devices */ 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 modem_shared *msd, @@ -204,12 +222,14 @@ void mif_add_timer(struct timer_list *timer, unsigned long expire, void (*function)(unsigned long), unsigned long data); /* debug helper functions for sipc4, sipc5 */ -void mif_print_data(char *buf, int len); +void mif_print_data(const char *buff, int len); +void mif_dump2format16(const char *data, int len, char *buff, char *tag); +void mif_dump2format4(const char *data, int len, char *buff, char *tag); +void mif_print_dump(const char *data, int len, int width); void print_sipc4_hdlc_fmt_frame(const u8 *psrc); void print_sipc4_fmt_frame(const u8 *psrc); void print_sipc5_link_fmt_frame(const u8 *psrc); - /*--------------------------------------------------------------------------- IPv4 Header Format @@ -282,23 +302,17 @@ void print_sipc5_link_fmt_frame(const u8 *psrc); -------------------------------------------------------------------------*/ #define UDP_HDR_SIZE 8 -void print_ip4_packet(u8 *ip_pkt, bool tx); -bool is_dns_packet(u8 *ip_pkt); -bool is_syn_packet(u8 *ip_pkt); +void print_ip4_packet(const u8 *ip_pkt, bool tx); +bool is_dns_packet(const u8 *ip_pkt); +bool is_syn_packet(const 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); +int mif_register_isr(unsigned int irq, irq_handler_t isr, unsigned long flags, + const char *name, void *data); +int mif_test_dpram(char *dp_name, void __iomem *start, u16 bytes); -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"; -} +struct file *mif_open_file(const char *path); +void mif_save_file(struct file *fp, const char *buff, size_t size); +void mif_close_file(struct file *fp); #endif/*__MODEM_UTILS_H__*/ + diff --git a/drivers/misc/modem_if/modem_variation.h b/drivers/misc/modem_if/modem_variation.h index b5ec61b..b314bb2 100644 --- a/drivers/misc/modem_if/modem_variation.h +++ b/drivers/misc/modem_if/modem_variation.h @@ -1,5 +1,4 @@ /* - * Copyright (C) 2010 Google, Inc. * Copyright (C) 2010 Samsung Electronics. * * This software is licensed under the terms of the GNU General Public @@ -16,7 +15,7 @@ #ifndef __MODEM_VARIATION_H__ #define __MODEM_VARIATION_H__ -#include <linux/platform_data/modem.h> +#include "modem.h" #define DECLARE_MODEM_INIT(type) \ int type ## _init_modemctl_device(struct modem_ctl *mc, \ @@ -61,12 +60,30 @@ DECLARE_MODEM_INIT(cbp72); DECLARE_MODEM_INIT_DUMMY(cbp72) #endif +#ifdef CONFIG_CDMA_MODEM_CBP82 +DECLARE_MODEM_INIT(cbp82); +#else +DECLARE_MODEM_INIT_DUMMY(cbp82) +#endif + +#ifdef CONFIG_LTE_MODEM_CMC220 +DECLARE_MODEM_INIT(cmc220); +#else +DECLARE_MODEM_INIT_DUMMY(cmc220) +#endif + #ifdef CONFIG_LTE_MODEM_CMC221 DECLARE_MODEM_INIT(cmc221); #else DECLARE_MODEM_INIT_DUMMY(cmc221) #endif +#ifdef CONFIG_UMTS_MODEM_SS222 +DECLARE_MODEM_INIT(ss222); +#else +DECLARE_MODEM_INIT_DUMMY(ss222) +#endif + #ifdef CONFIG_CDMA_MODEM_MDM6600 DECLARE_MODEM_INIT(mdm6600); #else @@ -79,6 +96,12 @@ DECLARE_MODEM_INIT(esc6270); DECLARE_MODEM_INIT_DUMMY(esc6270) #endif +#ifdef CONFIG_CDMA_MODEM_QSC6085 +DECLARE_MODEM_INIT(qsc6085); +#else +DECLARE_MODEM_INIT_DUMMY(qsc6085) +#endif + #ifdef CONFIG_TDSCDMA_MODEM_SPRD8803 DECLARE_MODEM_INIT(sprd8803); #else @@ -94,6 +117,18 @@ DECLARE_LINK_INIT(mipi); DECLARE_LINK_INIT_DUMMY(mipi) #endif +#ifdef CONFIG_LINK_DEVICE_USB +DECLARE_LINK_INIT(usb); +#else +DECLARE_LINK_INIT_DUMMY(usb) +#endif + +#ifdef CONFIG_LINK_DEVICE_HSIC +DECLARE_LINK_INIT(hsic); +#else +DECLARE_LINK_INIT_DUMMY(hsic) +#endif + #ifdef CONFIG_LINK_DEVICE_DPRAM DECLARE_LINK_INIT(dpram); #else @@ -106,53 +141,52 @@ DECLARE_LINK_INIT(pld); DECLARE_LINK_INIT_DUMMY(pld) #endif -#ifdef CONFIG_LINK_DEVICE_SPI -DECLARE_LINK_INIT(spi); -#else -DECLARE_LINK_INIT_DUMMY(spi) -#endif - -#ifdef CONFIG_LINK_DEVICE_USB -DECLARE_LINK_INIT(usb); +#ifdef CONFIG_LINK_DEVICE_C2C +DECLARE_LINK_INIT(c2c); #else -DECLARE_LINK_INIT_DUMMY(usb) +DECLARE_LINK_INIT_DUMMY(c2c) #endif -#ifdef CONFIG_LINK_DEVICE_HSIC -DECLARE_LINK_INIT(hsic); +#ifdef CONFIG_LINK_DEVICE_SHMEM +DECLARE_LINK_INIT(shmem); #else -DECLARE_LINK_INIT_DUMMY(hsic) +DECLARE_LINK_INIT_DUMMY(shmem) #endif -#ifdef CONFIG_LINK_DEVICE_C2C -DECLARE_LINK_INIT(c2c); +#ifdef CONFIG_LINK_DEVICE_SPI +DECLARE_LINK_INIT(spi); #else -DECLARE_LINK_INIT_DUMMY(c2c) +DECLARE_LINK_INIT_DUMMY(spi) #endif typedef int (*modem_init_call)(struct modem_ctl *, struct modem_data *); -static modem_init_call modem_init_func[] = { - MODEM_INIT_CALL(xmm6260), - MODEM_INIT_CALL(xmm6262), - MODEM_INIT_CALL(cbp71), - MODEM_INIT_CALL(cbp72), - MODEM_INIT_CALL(cmc221), - MODEM_INIT_CALL(mdm6600), - MODEM_INIT_CALL(esc6270), - MODEM_INIT_CALL(sprd8803), - MODEM_INIT_CALL(dummy), +static modem_init_call modem_init_func[MAX_MODEM_TYPE] = { + [IMC_XMM6260] = MODEM_INIT_CALL(xmm6260), + [IMC_XMM6262] = MODEM_INIT_CALL(xmm6262), + [VIA_CBP71] = MODEM_INIT_CALL(cbp71), + [VIA_CBP72] = MODEM_INIT_CALL(cbp72), + [VIA_CBP82] = MODEM_INIT_CALL(cbp82), + [SEC_CMC220] = MODEM_INIT_CALL(cmc220), + [SEC_CMC221] = MODEM_INIT_CALL(cmc221), + [SEC_SS222] = MODEM_INIT_CALL(ss222), + [QC_MDM6600] = MODEM_INIT_CALL(mdm6600), + [QC_ESC6270] = MODEM_INIT_CALL(esc6270), + [QC_QSC6085] = MODEM_INIT_CALL(qsc6085), + [SPRD_SC8803] = MODEM_INIT_CALL(sprd8803), + [DUMMY] = MODEM_INIT_CALL(dummy), }; typedef struct link_device *(*link_init_call)(struct platform_device *); -static link_init_call link_init_func[] = { - LINK_INIT_CALL(undefined), - LINK_INIT_CALL(mipi), - LINK_INIT_CALL(dpram), - LINK_INIT_CALL(spi), - LINK_INIT_CALL(usb), - LINK_INIT_CALL(hsic), - LINK_INIT_CALL(c2c), - LINK_INIT_CALL(pld), +static link_init_call link_init_func[LINKDEV_MAX] = { + [LINKDEV_UNDEFINED] = LINK_INIT_CALL(undefined), + [LINKDEV_MIPI] = LINK_INIT_CALL(mipi), + [LINKDEV_USB] = LINK_INIT_CALL(usb), + [LINKDEV_HSIC] = LINK_INIT_CALL(hsic), + [LINKDEV_DPRAM] = LINK_INIT_CALL(dpram), + [LINKDEV_PLD] = LINK_INIT_CALL(pld), + [LINKDEV_C2C] = LINK_INIT_CALL(c2c), + [LINKDEV_SHMEM] = LINK_INIT_CALL(shmem), + [LINKDEV_SPI] = LINK_INIT_CALL(spi), }; 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 28f95f7..da1ea04 100644 --- a/drivers/misc/modem_if/sipc4_io_device.c +++ b/drivers/misc/modem_if/sipc4_io_device.c @@ -1,6 +1,4 @@ -/* /linux/drivers/misc/modem_if/modem_io_device.c - * - * Copyright (C) 2010 Google, Inc. +/* * Copyright (C) 2010 Samsung Electronics. * * This software is licensed under the terms of the GNU General Public @@ -26,10 +24,7 @@ #include <linux/etherdevice.h> #include <linux/device.h> -#include <linux/platform_data/modem.h> -#ifdef CONFIG_LINK_DEVICE_C2C -#include <linux/platform_data/c2c.h> -#endif +#include "modem.h" #include "modem_prj.h" #include "modem_utils.h" @@ -40,7 +35,8 @@ * So, give restriction to allocation size below 1 page to prevent * big pages broken. */ -#define MAX_RXDATA_SIZE 0x0E00 /* 4 * 1024 - 512 */ +#define MAX_RXDATA_SIZE (4096 - 512) +#define MAX_BOOTDATA_SIZE 0x4008 /* EBL package format*/ #define MAX_MULTI_FMT_SIZE 0x4000 /* 16 * 1024 */ static const char hdlc_start[1] = { HDLC_START }; @@ -250,22 +246,6 @@ static int rx_hdlc_head_check(struct io_device *iod, struct link_device *ld, hdr->start = HDLC_START; hdr->len = 0; - /* debug print */ - switch (iod->format) { - case IPC_FMT: - case IPC_RAW: - case IPC_MULTI_RAW: - case IPC_RFS: - /* TODO: print buf... */ - break; - - case IPC_CMD: - case IPC_BOOT: - case IPC_RAMDUMP: - default: - break; - } - buf += len; done_len += len; rest -= len; /* rest, call by value */ @@ -409,12 +389,6 @@ static int rx_multi_fmt_frame(struct sk_buff *rx_skb) /* If there has been no multiple frame with this ID */ if (!(fh->control & 0x80)) { /* It is a single frame because the "more" bit is 0. */ -#if 0 - mif_err("\n<%s> Rx FMT frame (len %d)\n", - iod->name, rcvd); - print_sipc4_fmt_frame(data); - mif_err("\n"); -#endif skb_queue_tail(&iod->sk_rx_q, fragdata(iod, ld)->skb_recv); mif_debug("wake up wq of %s\n", iod->name); @@ -449,12 +423,6 @@ static int rx_multi_fmt_frame(struct sk_buff *rx_skb) /* It is the last frame because the "more" bit is 0. */ mif_info("The Last (ID %d, %d bytes received)\n", id, skb->len); -#if 0 - mif_err("\n<%s> Rx FMT frame (len %d)\n", - iod->name, skb->len); - print_sipc4_fmt_frame(skb->data); - mif_err("\n"); -#endif skb_queue_tail(&iod->sk_rx_q, skb); iod->skb[id] = NULL; mif_info("wake up wq of %s\n", iod->name); @@ -491,12 +459,6 @@ static int rx_multi_fmt_frame_sipc42(struct sk_buff *rx_skb) /* If there has been no multiple frame with this ID */ if (!(fh->control & 0x80)) { /* It is a single frame because the "more" bit is 0. */ -#if 0 - mif_err("\n<%s> Rx FMT frame (len %d)\n", - iod->name, rcvd); - print_sipc4_fmt_frame(data); - mif_err("\n"); -#endif skb_queue_tail(&real_iod->sk_rx_q, fragdata(iod, ld)->skb_recv); mif_debug("wake up wq of %s\n", iod->name); @@ -530,12 +492,6 @@ static int rx_multi_fmt_frame_sipc42(struct sk_buff *rx_skb) /* It is the last frame because the "more" bit is 0. */ mif_err("The Last (ID %d, %d bytes received)\n", id, skb->len); -#if 0 - mif_err("\n<%s> Rx FMT frame (len %d)\n", - iod->name, skb->len); - print_sipc4_fmt_frame(skb->data); - mif_err("\n"); -#endif skb_queue_tail(&real_iod->sk_rx_q, skb); real_iod->skb[id] = NULL; mif_info("wake up wq of %s\n", real_iod->name); @@ -819,55 +775,6 @@ exit: return err; } -static int rx_rfs_packet(struct io_device *iod, struct link_device *ld, - const char *data, unsigned size) -{ - int err = 0; - int pad = 0; - int rcvd = 0; - struct sk_buff *skb; - - if (data[0] != HDLC_START) { - mif_err("Dropping RFS packet ... " - "size = %d, start = %02X %02X %02X %02X\n", - size, - data[0], data[1], data[2], data[3]); - return -EINVAL; - } - - if (data[size-1] != HDLC_END) { - for (pad = 1; pad < 4; pad++) - if (data[(size-1)-pad] == HDLC_END) - break; - - if (pad >= 4) { - char *b = (char *)data; - unsigned sz = size; - mif_err("size %d, No END_FLAG!!!\n", size); - mif_err("end = %02X %02X %02X %02X\n", - b[sz-4], b[sz-3], b[sz-2], b[sz-1]); - return -EINVAL; - } else { - mif_info("padding = %d\n", pad); - } - } - - skb = rx_alloc_skb(size, iod, ld); - if (unlikely(!skb)) { - mif_err("alloc_skb fail\n"); - return -ENOMEM; - } - - /* copy the RFS haeder to skb->data */ - rcvd = size - sizeof(hdlc_start) - sizeof(hdlc_end) - pad; - memcpy(skb_put(skb, rcvd), ((char *)data + sizeof(hdlc_start)), rcvd); - - fragdata(iod, ld)->skb_recv = skb; - err = rx_iodev_skb(fragdata(iod, ld)->skb_recv); - - return err; -} - /* called from link device when a packet arrives for this io device */ static int io_dev_recv_data_from_link_dev(struct io_device *iod, struct link_device *ld, const char *data, unsigned int len) @@ -891,14 +798,9 @@ static int io_dev_recv_data_from_link_dev(struct io_device *iod, */ switch (iod->format) { - case IPC_RFS: -#ifdef CONFIG_IPC_CMC22x_OLD_RFS - err = rx_rfs_packet(iod, ld, data, len); - return err; -#endif - case IPC_FMT: case IPC_RAW: + case IPC_RFS: case IPC_MULTI_RAW: if (iod->waketime) wake_lock_timeout(&iod->wakelock, iod->waketime); @@ -961,12 +863,17 @@ static int io_dev_recv_data_from_link_dev(struct io_device *iod, static void io_dev_modem_state_changed(struct io_device *iod, enum modem_state state) { - iod->mc->phone_state = state; - mif_err("modem state changed. (iod: %s, state: %d)\n", - iod->name, state); + struct modem_ctl *mc = iod->mc; + int old_state = mc->phone_state; - if ((state == STATE_CRASH_RESET) || (state == STATE_CRASH_EXIT) - || (state == STATE_NV_REBUILDING)) + if (old_state != state) { + mc->phone_state = state; + mif_err("%s state changed (%s -> %s)\n", mc->name, + get_cp_state_str(old_state), get_cp_state_str(state)); + } + + if (state == STATE_CRASH_RESET || state == STATE_CRASH_EXIT || + state == STATE_NV_REBUILDING) wake_up(&iod->wq); } @@ -977,10 +884,24 @@ static void io_dev_modem_state_changed(struct io_device *iod, */ static void io_dev_sim_state_changed(struct io_device *iod, bool sim_online) { + +#if defined(CONFIG_MACH_KONA) && defined(CONFIG_UMTS_MODEM_XMM6262) + mif_err("modem_current_state is %d\n", iod->mc->phone_state); +#endif + if (atomic_read(&iod->opened) == 0) { - mif_err("iod is not opened: %s\n", - iod->name); - } else if (iod->mc->sim_state.online == sim_online) { + mif_err("iod is not opened: %s\n", iod->name); + /* update latest sim status */ + iod->mc->sim_state.online = sim_online; + } +#if defined(CONFIG_LINK_DEVICE_HSIC) && defined(CONFIG_UMTS_MODEM_XMM6262) // fixed modem unknown issue (kina 3G) + else if (iod->mc->phone_state == STATE_BOOTING) { + mif_err("modem_current_state is STATE_BOOTING\n"); + /* update latest sim status */ + iod->mc->sim_state.online = sim_online; + } +#endif + else if (iod->mc->sim_state.online == sim_online) { mif_err("sim state not changed.\n"); } else { iod->mc->sim_state.online = sim_online; @@ -995,6 +916,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); @@ -1078,32 +1000,32 @@ static long misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) switch (cmd) { case IOCTL_MODEM_ON: - mif_debug("misc_ioctl : IOCTL_MODEM_ON\n"); + mif_debug("%s: IOCTL_MODEM_ON\n", iod->name); return iod->mc->ops.modem_on(iod->mc); case IOCTL_MODEM_OFF: - mif_debug("misc_ioctl : IOCTL_MODEM_OFF\n"); + mif_debug("%s: IOCTL_MODEM_OFF\n", iod->name); return iod->mc->ops.modem_off(iod->mc); case IOCTL_MODEM_RESET: - mif_debug("misc_ioctl : IOCTL_MODEM_RESET\n"); + mif_debug("%s: IOCTL_MODEM_RESET\n", iod->name); return iod->mc->ops.modem_reset(iod->mc); case IOCTL_MODEM_BOOT_ON: - mif_debug("misc_ioctl : IOCTL_MODEM_BOOT_ON\n"); + mif_debug("%s: IOCTL_MODEM_BOOT_ON\n", iod->name); return iod->mc->ops.modem_boot_on(iod->mc); case IOCTL_MODEM_BOOT_OFF: - mif_debug("misc_ioctl : IOCTL_MODEM_BOOT_OFF\n"); + mif_debug("%s: IOCTL_MODEM_BOOT_OFF\n", iod->name); return iod->mc->ops.modem_boot_off(iod->mc); /* TODO - will remove this command after ril updated */ case IOCTL_MODEM_BOOT_DONE: - mif_debug("misc_ioctl : IOCTL_MODEM_BOOT_DONE\n"); + mif_debug("%s: IOCTL_MODEM_BOOT_DONE\n", iod->name); return 0; case IOCTL_MODEM_STATUS: - mif_debug("misc_ioctl : IOCTL_MODEM_STATUS\n"); + mif_debug("%s: IOCTL_MODEM_STATUS\n", iod->name); p_state = iod->mc->phone_state; if ((p_state == STATE_CRASH_RESET) || @@ -1126,7 +1048,7 @@ static long misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) return p_state; case IOCTL_MODEM_PROTOCOL_SUSPEND: - mif_info("misc_ioctl : IOCTL_MODEM_PROTOCOL_SUSPEND\n"); + mif_info("%s: IOCTL_MODEM_PROTOCOL_SUSPEND\n", iod->name); if (iod->format != IPC_MULTI_RAW) return -EINVAL; @@ -1134,8 +1056,16 @@ static long misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) iodevs_for_each(iod->msd, iodev_netif_stop, 0); return 0; + case IOCTL_MODEM_DL_START: + mif_info("%s: IOCTL_MODEM_DL_START\n", iod->name); + return ld->dload_start(ld, iod); + + case IOCTL_MODEM_FW_UPDATE: + mif_info("%s: IOCTL_MODEM_FW_UPDATE\n", iod->name); + return ld->firm_update(ld, iod, arg); + case IOCTL_MODEM_PROTOCOL_RESUME: - mif_info("misc_ioctl : IOCTL_MODEM_PROTOCOL_RESUME\n"); + mif_info("%s: IOCTL_MODEM_PROTOCOL_RESUME\n", iod->name); if (iod->format != IPC_MULTI_RAW) return -EINVAL; @@ -1144,21 +1074,29 @@ static long misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) return 0; case IOCTL_MODEM_DUMP_START: - mif_err("misc_ioctl : IOCTL_MODEM_DUMP_START\n"); + mif_err("%s: IOCTL_MODEM_DUMP_START\n", iod->name); + return ld->dump_start(ld, iod); + + case IOCTL_MODEM_RAMDUMP_START: + mif_err("%s: IOCTL_MODEM_RAMDUMP_START\n", iod->name); return ld->dump_start(ld, iod); case IOCTL_MODEM_DUMP_UPDATE: - mif_debug("misc_ioctl : IOCTL_MODEM_DUMP_UPDATE\n"); + mif_debug("%s: IOCTL_MODEM_DUMP_UPDATE\n", iod->name); return ld->dump_update(ld, iod, arg); + case IOCTL_MODEM_RAMDUMP_STOP: + mif_info("%s: IOCTL_MODEM_RAMDUMP_STOP\n", iod->name); + return ld->dump_finish(ld, iod, arg); + case IOCTL_MODEM_FORCE_CRASH_EXIT: - mif_debug("misc_ioctl : IOCTL_MODEM_FORCE_CRASH_EXIT\n"); + mif_debug("%s: IOCTL_MODEM_FORCE_CRASH_EXIT\n", iod->name); if (iod->mc->ops.modem_force_crash_exit) return iod->mc->ops.modem_force_crash_exit(iod->mc); return -EINVAL; case IOCTL_MODEM_CP_UPLOAD: - mif_err("misc_ioctl : IOCTL_MODEM_CP_UPLOAD\n"); + mif_err("%s: IOCTL_MODEM_CP_UPLOAD\n", iod->name); if (copy_from_user(cpinfo_buf + strlen(cpinfo_buf), (void __user *)arg, MAX_CPINFO_SIZE) != 0) panic("CP Crash"); @@ -1167,12 +1105,12 @@ static long misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) return 0; case IOCTL_MODEM_DUMP_RESET: - mif_err("misc_ioctl : IOCTL_MODEM_DUMP_RESET\n"); + mif_err("%s: IOCTL_MODEM_DUMP_RESET\n", iod->name); 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"); + mif_err("%s: IOCTL_MODEM_SWITCH_MODEM\n", iod->name); iod->mc->phone_state = STATE_MODEM_SWITCH; wake_up(&iod->wq); return 0; @@ -1188,20 +1126,6 @@ static long misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) 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 @@ -1209,12 +1133,48 @@ static long misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) if (ld->ioctl) return ld->ioctl(ld, iod, cmd, arg); - mif_err("misc_ioctl : ioctl 0x%X is not defined.\n", cmd); + mif_err("%s: ioctl 0x%X is not defined\n", iod->name, cmd); return -EINVAL; } return 0; } +static size_t _boot_write(struct io_device *iod, const char __user *buf, + size_t count) +{ + int rest_len = count, frame_len = 0; + char *cur = (char *)buf; + struct sk_buff *skb = NULL; + struct link_device *ld = get_current_link(iod); + int ret; + + while (rest_len) { + frame_len = min(rest_len, MAX_BOOTDATA_SIZE); + skb = alloc_skb(frame_len, GFP_KERNEL); + if (!skb) { + mif_err("fail alloc skb (%d)\n", __LINE__); + return -ENOMEM; + } + if (copy_from_user( + skb_put(skb, frame_len), cur, frame_len) != 0) { + dev_kfree_skb_any(skb); + return -EFAULT; + } + rest_len -= frame_len; + cur += frame_len; + + skbpriv(skb)->iod = iod; + skbpriv(skb)->ld = ld; + + ret = ld->send(ld, iod, skb); + if (ret < 0) { + dev_kfree_skb_any(skb); + return ret; + } + } + return count; +} + static ssize_t misc_write(struct file *filp, const char __user *buf, size_t count, loff_t *ppos) { @@ -1237,6 +1197,10 @@ static ssize_t misc_write(struct file *filp, const char __user *buf, skb = alloc_skb(frame_len, GFP_KERNEL); if (!skb) { + if (frame_len > MAX_BOOTDATA_SIZE && iod->format == IPC_BOOT) { + mif_info("large alloc fail\n"); + return _boot_write(iod, buf, count); + } mif_err("fail alloc skb (%d)\n", __LINE__); return -ENOMEM; } @@ -1278,31 +1242,6 @@ static ssize_t misc_write(struct file *filp, const char __user *buf, skb_put(skb, calc_padding_size(iod, ld, skb->len)); -#if 0 - if (iod->format == IPC_FMT) { - mif_err("\n<%s> Tx HDLC FMT frame (len %d)\n", - iod->name, skb->len); - print_sipc4_hdlc_fmt_frame(skb->data); - mif_err("\n"); - } -#endif -#if 0 - if (iod->format == IPC_RAW) { - mif_err("\n<%s> Tx HDLC RAW frame (len %d)\n", - iod->name, skb->len); - mif_print_data(skb->data, (skb->len < 64 ? skb->len : 64)); - mif_err("\n"); - } -#endif -#if 0 - if (iod->format == IPC_RFS) { - mif_err("\n<%s> Tx HDLC RFS frame (len %d)\n", - iod->name, skb->len); - mif_print_data(skb->data, (skb->len < 64 ? skb->len : 64)); - mif_err("\n"); - } -#endif - /* send data with sk_buff, link device will put sk_buff * into the specific sk_buff_q and run work-q to send data */ @@ -1409,43 +1348,6 @@ static ssize_t misc_read(struct file *filp, char *buf, size_t count, return pktsize; } -#ifdef CONFIG_LINK_DEVICE_C2C -static int misc_mmap(struct file *filp, struct vm_area_struct *vma) -{ - int r = 0; - unsigned long size = 0; - unsigned long pfn = 0; - unsigned long offset = 0; - struct io_device *iod = (struct io_device *)filp->private_data; - - if (!vma) - return -EFAULT; - - size = vma->vm_end - vma->vm_start; - offset = vma->vm_pgoff << PAGE_SHIFT; - if (offset + size > (C2C_CP_RGN_SIZE + C2C_SH_RGN_SIZE)) { - mif_err("offset + size > C2C_CP_RGN_SIZE\n"); - return -EINVAL; - } - - /* Set the noncacheable property to the region */ - vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); - vma->vm_flags |= VM_RESERVED | VM_IO; - - pfn = __phys_to_pfn(C2C_CP_RGN_ADDR + offset); - r = remap_pfn_range(vma, vma->vm_start, pfn, size, vma->vm_page_prot); - if (r) { - mif_err("Failed in remap_pfn_range()!!!\n"); - return -EAGAIN; - } - - mif_err("VA = 0x%08lx, offset = 0x%lx, size = %lu\n", - vma->vm_start, offset, size); - - return 0; -} -#endif - static const struct file_operations misc_io_fops = { .owner = THIS_MODULE, .open = misc_open, @@ -1454,9 +1356,6 @@ static const struct file_operations misc_io_fops = { .unlocked_ioctl = misc_ioctl, .write = misc_write, .read = misc_read, -#ifdef CONFIG_LINK_DEVICE_C2C - .mmap = misc_mmap, -#endif }; static int vnet_open(struct net_device *ndev) diff --git a/drivers/misc/modem_if/sipc4_modem.c b/drivers/misc/modem_if/sipc4_modem.c index 59e2de9..b4f9c2a 100644 --- a/drivers/misc/modem_if/sipc4_modem.c +++ b/drivers/misc/modem_if/sipc4_modem.c @@ -32,7 +32,7 @@ #include <linux/delay.h> #include <linux/wakelock.h> -#include <linux/platform_data/modem.h> +#include "modem.h" #include "modem_prj.h" #include "modem_variation.h" #include "modem_utils.h" @@ -328,9 +328,22 @@ static int modem_resume(struct device *pdev) return 0; } +#ifdef CONFIG_FAST_BOOT +static void modem_complete(struct device *pdev) +{ + struct modem_ctl *mc = dev_get_drvdata(pdev); + + if (mc->modem_complete) + mc->modem_complete(mc); +} +#endif + static const struct dev_pm_ops modem_pm_ops = { .suspend = modem_suspend, .resume = modem_resume, +#ifdef CONFIG_FAST_BOOT + .complete = modem_complete, +#endif }; static struct platform_driver modem_driver = { diff --git a/drivers/misc/modem_if/sipc5_common.c b/drivers/misc/modem_if/sipc5_common.c new file mode 100644 index 0000000..e8574c2 --- /dev/null +++ b/drivers/misc/modem_if/sipc5_common.c @@ -0,0 +1,239 @@ +/* + * 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/sched.h> +#include <linux/fs.h> +#include <linux/poll.h> +#include <linux/irq.h> +#include <linux/gpio.h> +#include <linux/if_arp.h> +#include <linux/ip.h> +#include <linux/if_ether.h> +#include <linux/etherdevice.h> +#include <linux/device.h> + +#include "modem.h" +#include "modem_prj.h" +#include "modem_utils.h" + +/** + * 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. + * + */ +bool sipc5_start_valid(u8 *frm) +{ + return (*frm & SIPC5_START_MASK) == SIPC5_START_MASK; +} + +bool sipc5_padding_exist(u8 *frm) +{ + return (*frm & SIPC5_PADDING_EXIST) ? true : false; +} + +bool sipc5_multi_frame(u8 *frm) +{ + return (*frm & SIPC5_EXT_FIELD_MASK) == SIPC5_CTL_FIELD_MASK; +} + +bool sipc5_ext_len(u8 *frm) +{ + return (*frm & SIPC5_EXT_FIELD_MASK) == SIPC5_EXT_LENGTH_MASK; +} + +/** + * 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 + * + */ +int sipc5_get_hdr_len(u8 *frm) +{ + if (*frm & SIPC5_EXT_FIELD_EXIST) { + if (*frm & 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; + } +} + +/** + * sipc5_get_ch_id + * @frm: pointer to an SIPC5 frame + * + * Returns the channel ID in an SIPC5 link frame + * + */ +u8 sipc5_get_ch_id(u8 *frm) +{ + return *(frm + SIPC5_CH_ID_OFFSET); +} + +/** + * sipc5_get_ctrl_field + * @frm: pointer to an SIPC5 frame + * + * Returns the control field in an SIPC5 link frame + * + */ +u8 sipc5_get_ctrl_field(u8 *frm) +{ + return *(frm + SIPC5_CTL_OFFSET); +} + +/** + * sipc5_get_frame_len + * @frm: pointer to an SIPC5 link frame + * + * Returns the length of an SIPC5 link frame + * + */ +int sipc5_get_frame_len(u8 *frm) +{ + u8 cfg = frm[0]; + u16 *sz16 = (u16 *)(frm + SIPC5_LEN_OFFSET); + u32 *sz32 = (u32 *)(frm + SIPC5_LEN_OFFSET); + + if (unlikely(cfg & SIPC5_EXT_FIELD_EXIST)) { + if (cfg & SIPC5_CTL_FIELD_EXIST) + return (int)(*sz16); + else + return (int)(*sz32); + } else { + return (int)(*sz16); + } +} + +/** + * sipc5_calc_padding_size + * @len: length of an SIPC5 link frame + * + * Returns the padding size for an SIPC5 link frame + * + */ +int sipc5_calc_padding_size(int len) +{ + int residue = len & 0x3; + return residue ? (4 - residue) : 0; +} + +/** + * sipc5_get_total_len + * @frm: pointer to an SIPC5 link frame + * + * Returns the total length of an SIPC5 link frame with padding + * + */ +int sipc5_get_total_len(u8 *frm) +{ + int len = sipc5_get_frame_len(frm); + int pad = sipc5_padding_exist(frm) ? sipc5_calc_padding_size(len) : 0; + return len + pad; +} + +/** + * sipc5_build_config + * @iod: pointer to the IO device + * @ld: pointer to the link device + * @count: length of the data to be transmitted + * + * Builds a config value for an SIPC5 link frame header + * + * Returns the config value for the header or 0 for non-SIPC formats + */ +u8 sipc5_build_config(struct io_device *iod, struct link_device *ld, u32 count) +{ + u8 cfg = SIPC5_START_MASK; + + if (iod->format > IPC_MULTI_RAW && iod->id == 0) + return 0; + + if (ld->aligned) + cfg |= SIPC5_PADDING_EXIST; + +#if 0 + if ((count + SIPC5_MIN_HEADER_SIZE) > ld->mtu[dev]) + cfg |= SIPC5_CTL_FIELD_MASK; + else +#endif + if ((count + SIPC5_MIN_HEADER_SIZE) > 0xFFFF) + cfg |= SIPC5_EXT_LENGTH_MASK; + + return cfg; +} + +/** + * sipc5_build_header + * @iod: pointer to the IO device + * @ld: pointer to the link device + * @buff: pointer to a buffer in which an SIPC5 link frame header will be stored + * @cfg: value for the config field in the header + * @ctrl: value for the control field in the header + * @count: length of data in the SIPC5 link frame to be transmitted + * + * Builds the link layer header of an SIPC5 frame + */ +void sipc5_build_header(struct io_device *iod, struct link_device *ld, + u8 *buff, u8 cfg, u8 ctrl, u32 count) +{ + u16 *sz16 = (u16 *)(buff + SIPC5_LEN_OFFSET); + u32 *sz32 = (u32 *)(buff + SIPC5_LEN_OFFSET); + u32 hdr_len = sipc5_get_hdr_len(&cfg); + + /* Store the config field and the channel ID field */ + buff[SIPC5_CONFIG_OFFSET] = cfg; + buff[SIPC5_CH_ID_OFFSET] = iod->id; + + /* Store the frame length field */ + if (sipc5_ext_len(buff)) + *sz32 = (u32)(hdr_len + count); + else + *sz16 = (u16)(hdr_len + count); + + /* Store the control field */ + if (sipc5_multi_frame(buff)) + buff[SIPC5_CTL_OFFSET] = ctrl; +} + +/** + * std_udl_get_cmd + * @frm: pointer to an SIPC5 link frame + * + * Returns the standard BOOT/DUMP (STD_UDL) command in an SIPC5 BOOT/DUMP frame. + */ +u32 std_udl_get_cmd(u8 *frm) +{ + u8 *cmd = frm + sipc5_get_hdr_len(frm); + return *((u32 *)cmd); +} + +/** + * std_udl_with_payload + * @cmd: standard BOOT/DUMP command + * + * Returns true if the STD_UDL command has a payload. + */ +bool std_udl_with_payload(u32 cmd) +{ + u32 mask = cmd & STD_UDL_STEP_MASK; + return (mask && mask < STD_UDL_CRC) ? true : false; +} + diff --git a/drivers/misc/modem_if/sipc5_io_device.c b/drivers/misc/modem_if/sipc5_io_device.c index a9932c1..71596ae 100644 --- a/drivers/misc/modem_if/sipc5_io_device.c +++ b/drivers/misc/modem_if/sipc5_io_device.c @@ -1,5 +1,4 @@ -/* /linux/drivers/misc/modem_if/sipc5_io_device.c - * +/* * Copyright (C) 2010 Samsung Electronics. * * This software is licensed under the terms of the GNU General Public @@ -24,11 +23,9 @@ #include <linux/if_ether.h> #include <linux/etherdevice.h> #include <linux/device.h> +#include <linux/module.h> -#include <linux/platform_data/modem.h> -#ifdef CONFIG_LINK_DEVICE_C2C -#include <linux/platform_data/c2c.h> -#endif +#include "modem.h" #include "modem_prj.h" #include "modem_utils.h" @@ -104,7 +101,7 @@ static void iodev_showtxlink(struct io_device *iod, void *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); + *p += sprintf(*p, "%s<->%s\n", iod->name, ld->name); } static ssize_t show_txlink(struct device *dev, @@ -130,181 +127,107 @@ static ssize_t store_txlink(struct device *dev, static struct device_attribute attr_txlink = __ATTR(txlink, S_IRUGO | S_IWUSR, show_txlink, store_txlink); -/** - * 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) +static int netif_flow_ctrl(struct link_device *ld, struct sk_buff *skb) { - frm->config = cfg; - - if (likely(cfg & SIPC5_PADDING_EXIST)) - frm->padding = true; - - 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; - } + u8 cmd = skb->data[0]; + + if (cmd == FLOW_CTRL_SUSPEND) { + if (ld->suspend_netif_tx) + goto exit; + ld->suspend_netif_tx = true; + mif_netif_stop(ld); + mif_info("%s: FLOW_CTRL_SUSPEND\n", ld->name); + } else if (cmd == FLOW_CTRL_RESUME) { + if (!ld->suspend_netif_tx) + goto exit; + ld->suspend_netif_tx = false; + mif_netif_wake(ld); + mif_info("%s: FLOW_CTRL_RESUME\n", ld->name); } else { - frm->hdr_len = SIPC5_MIN_HEADER_SIZE; + mif_info("%s: ERR! invalid command %02X\n", ld->name, cmd); } - return frm->hdr_len; -} - -/** - * 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]; - - 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; - - mif_debug("%s: FRM ch:%d len:%d ctl:%02X data.len:%d\n", - ld->name, frm->ch_id, frm->len, frm->control, frm->data_len); +exit: + dev_kfree_skb_any(skb); + return 0; } -/** - * 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) +static inline int queue_skb_to_iod(struct sk_buff *skb, struct io_device *iod) { - u8 *buff = frm->hdr; - u16 *sz16 = (u16 *)(buff + SIPC5_LEN_OFFSET); - u32 *sz32 = (u32 *)(buff + SIPC5_LEN_OFFSET); + struct sk_buff_head *rxq = &iod->sk_rx_q; - memset(frm, 0, sizeof(struct sipc5_frame_data)); + skb_queue_tail(rxq, skb); - if (iod->format == IPC_CMD || - iod->format == IPC_BOOT || - iod->format == IPC_RAMDUMP) { - frm->len = count; + if (iod->format < IPC_MULTI_RAW && rxq->qlen > MAX_IOD_RXQ_LEN) { + struct sk_buff *victim = skb_dequeue(rxq); + mif_err("%s: %s application may be dead (rxq->qlen %d > %d)\n", + iod->name, iod->app ? iod->app : "corresponding", + rxq->qlen, MAX_IOD_RXQ_LEN); + if (victim) + dev_kfree_skb_any(victim); + return -ENOSPC; + } else { + mif_debug("%s: rxq->qlen = %d\n", iod->name, rxq->qlen); return 0; } +} - frm->config = SIPC5_START_MASK; +static int rx_drain(struct sk_buff *skb) +{ + dev_kfree_skb_any(skb); + return 0; +} - if (iod->format == IPC_FMT && count > 2048) { - frm->ctl_fld = true; - frm->config |= SIPC5_EXT_FIELD_EXIST; - frm->config |= SIPC5_CTL_FIELD_EXIST; - } +static int rx_loopback(struct sk_buff *skb) +{ + struct io_device *iod = skbpriv(skb)->iod; + struct link_device *ld = skbpriv(skb)->ld; + int ret; - if (iod->id >= SIPC5_CH_ID_RFS_0 && count > 0xFFFF) { - frm->ext_len = true; - frm->config |= SIPC5_EXT_FIELD_EXIST; + ret = ld->send(ld, iod, skb); + if (ret < 0) { + mif_err("%s->%s: ERR! ld->send fail (err %d)\n", + iod->name, ld->name, ret); } - if (ld->aligned) - frm->config |= SIPC5_PADDING_EXIST; - - frm->ch_id = iod->id; - - frm->hdr_len = sipc5_get_hdr_len(frm->config); - frm->data_len = count; - frm->len = frm->hdr_len + frm->data_len; - - buff[SIPC5_CONFIG_OFFSET] = frm->config; - buff[SIPC5_CH_ID_OFFSET] = frm->ch_id; - - if (unlikely(frm->ext_len)) - *sz32 = (u32)frm->len; - else - *sz16 = (u16)frm->len; - - if (unlikely(frm->ctl_fld)) - buff[SIPC5_CTL_OFFSET] = frm->control; - - return frm->hdr_len; + return ret; } 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 = &iod->sk_rx_q; - struct sipc_fmt_hdr *fh; + struct io_device *iod = skbpriv(skb)->iod; struct sk_buff *rx_skb; - u8 ctrl = skbpriv(skb)->control; - unsigned id = ctrl & 0x7F; + int hdr_len = sipc5_get_hdr_len(skb->data); + u8 ctrl; + u8 id; - if (iod->skb[id] == NULL) { - /* - ** There has been no multiple frame with this ID. - */ - if ((ctrl & 0x80) == 0) { - /* - ** It is a single frame because the "more" bit is 0. - */ - skb_queue_tail(rxq, skb); - if (unlikely(rxq->qlen > 2048)) { - struct sk_buff *victim; - mif_info("%s: WARNING! rxq->qlen %d > 2048\n", - iod->name, rxq->qlen); - victim = skb_dequeue(rxq); - dev_kfree_skb_any(victim); - } else { - mif_debug("%s: rxq->qlen = %d\n", - iod->name, rxq->qlen); - } + if (!sipc5_multi_frame(skb->data)) { + skb_pull(skb, hdr_len); + queue_skb_to_iod(skb, iod); + wake_up(&iod->wq); + return 0; + } - wake_up(&iod->wq); - return 0; - } + /* Get the control field */ + ctrl = sipc5_get_ctrl_field(skb->data); - /* - ** The start of multiple frames - */ - fh = (struct sipc_fmt_hdr *)skb->data; - mif_debug("%s: start multi-frame (ID:%d len:%d)\n", - iod->name, id, fh->len); + /* Extract the control ID from the control field */ + id = ctrl & 0x7F; + + /* Remove SIPC5 link header */ + skb_pull(skb, hdr_len); + + /* If there has been no multiple frame with this ID, ... */ + if (iod->skb[id] == NULL) { + struct sipc_fmt_hdr *fh = (struct sipc_fmt_hdr *)skb->data; + + mif_err("%s->%s: start of multi-frame (ID:%d len:%d)\n", + ld->name, iod->name, id, fh->len); rx_skb = rx_alloc_skb(fh->len, iod, ld); if (!rx_skb) { - mif_info("%s: ERR! rx_alloc_skb fail\n", iod->name); + mif_err("%s: ERR! rx_alloc_skb fail\n", iod->name); return -ENOMEM; } @@ -313,31 +236,19 @@ static int rx_fmt_frame(struct sk_buff *skb) rx_skb = iod->skb[id]; } - /* - ** Start multi-frame processing - */ + /* Perform multi-frame processing */ memcpy(skb_put(rx_skb, skb->len), skb->data, skb->len); dev_kfree_skb_any(skb); 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); + mif_info("%s->%s: recv multi-frame (ID:%d rcvd:%d)\n", + ld->name, iod->name, id, rx_skb->len); } else { /* It is the last frame because the "more" bit is 0. */ - mif_debug("%s: end multi-frame (ID:%d rcvd:%d)\n", - iod->name, id, rx_skb->len); - skb_queue_tail(rxq, rx_skb); - if (unlikely(rxq->qlen > 2048)) { - struct sk_buff *victim; - mif_info("%s: WARNING! rxq->qlen %d > 2048\n", - iod->name, rxq->qlen); - victim = skb_dequeue(rxq); - dev_kfree_skb_any(victim); - } else { - mif_debug("%s: rxq->qlen = %d\n", iod->name, rxq->qlen); - } - + mif_err("%s->%s: end of multi-frame (ID:%d rcvd:%d)\n", + ld->name, iod->name, id, rx_skb->len); + queue_skb_to_iod(rx_skb, iod); iod->skb[id] = NULL; wake_up(&iod->wq); } @@ -345,76 +256,14 @@ static int rx_fmt_frame(struct sk_buff *skb) return 0; } -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; - - skb_queue_tail(rxq, skb); - if (unlikely(rxq->qlen > 2048)) { - struct sk_buff *victim; - mif_debug("%s: rxq->qlen %d > 2048\n", iod->name, rxq->qlen); - victim = skb_dequeue(rxq); - dev_kfree_skb_any(victim); - } else { - mif_debug("%s: rxq->qlen %d\n", iod->name, rxq->qlen); - } - - wake_up(&iod->wq); - - return 0; -} - -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; + struct io_device *iod = skbpriv(skb)->iod; - skb_queue_tail(rxq, skb); - if (unlikely(rxq->qlen > 2048)) { - struct sk_buff *victim; - mif_debug("%s: rxq->qlen %d > 2048\n", iod->name, rxq->qlen); - victim = skb_dequeue(rxq); - dev_kfree_skb_any(victim); - } else { - mif_debug("%s: rxq->qlen %d\n", iod->name, rxq->qlen); - } + /* Remove the SIPC5 link header */ + skb_pull(skb, sipc5_get_hdr_len(skb->data)); + queue_skb_to_iod(skb, iod); wake_up(&iod->wq); return 0; @@ -422,12 +271,11 @@ static int rx_raw_misc(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 link_device *ld = skbpriv(skb)->ld; + struct io_device *iod = skbpriv(skb)->iod; struct net_device *ndev; struct iphdr *iphdr; - struct ethhdr *ehdr; int ret; - const char source[ETH_ALEN] = SOURCE_MAC_ADDR; ndev = iod->ndev; if (!ndev) { @@ -435,6 +283,9 @@ static int rx_multi_pdp(struct sk_buff *skb) return -ENODEV; } + /* Remove the SIPC5 link header */ + skb_pull(skb, sipc5_get_hdr_len(skb->data)); + skb->dev = ndev; ndev->stats.rx_packets++; ndev->stats.rx_bytes += skb->len; @@ -447,14 +298,15 @@ static int rx_multi_pdp(struct sk_buff *skb) skb->protocol = htons(ETH_P_IP); if (iod->use_handover) { - skb_push(skb, sizeof(struct ethhdr)); - ehdr = (void *)skb->data; + struct ethhdr *ehdr; + const char source[ETH_ALEN] = SOURCE_MAC_ADDR; + + ehdr = (struct ethhdr *)skb_push(skb, sizeof(struct ethhdr)); memcpy(ehdr->h_dest, ndev->dev_addr, ETH_ALEN); memcpy(ehdr->h_source, source, ETH_ALEN); ehdr->h_proto = skb->protocol; skb->ip_summed = CHECKSUM_UNNECESSARY; skb_reset_mac_header(skb); - skb_pull(skb, sizeof(struct ethhdr)); } @@ -463,48 +315,82 @@ static int rx_multi_pdp(struct sk_buff *skb) else ret = netif_rx_ni(skb); - if (ret != NET_RX_SUCCESS) - mif_info("%s: ERR! netif_rx fail (err %d)\n", iod->name, ret); + if (ret != NET_RX_SUCCESS) { + mif_err("%s->%s: ERR! netif_rx fail (err %d)\n", + ld->name, iod->name, ret); + } return ret; } 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; + struct io_device *iod; + u8 ch = sipc5_get_ch_id(skb->data); +#ifdef DEBUG_MODEM_IF + struct modem_ctl *mc = ld->mc; + size_t len = (skb->len > 20) ? 20 : skb->len; + char tag[MIF_MAX_STR_LEN]; +#endif - if (unlikely(ch == SIPC5_CH_ID_MAX || ch == 0)) { - mif_info("%s: ERR! invalid ch# %d\n", link, ch); + if (unlikely(ch == 0)) { + mif_err("%s: ERR! invalid ch# %d\n", ld->name, ch); return -ENODEV; } + if (unlikely(ch == SIPC5_CH_ID_FLOW_CTRL)) + return netif_flow_ctrl(ld, skb); + /* 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", link, ch); + mif_err("%s: ERR! no iod with ch# %d\n", ld->name, ch); return -ENODEV; } skbpriv(skb)->ld = ld; 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)) + /* Don't care whether or not DATA_DRAIN_CHANNEL is opened */ + if (iod->id == DATA_DRAIN_CHANNEL) + return rx_drain(skb); + + /* Don't care whether or not DATA_LOOPBACK_CHANNEL is opened */ + if (iod->id == DATA_LOOPBACK_CHANNEL) return rx_loopback(skb); +#ifdef DEBUG_MODEM_IF + snprintf(tag, MIF_MAX_STR_LEN, "LNK: %s->%s", mc->name, iod->name); + if (unlikely(iod->format == IPC_FMT)) + pr_ipc(1, tag, skb->data, len); +#if 0 + if (iod->format == IPC_RAW) + pr_ipc(0, tag, skb->data, len); +#endif +#if 0 + if (iod->format == IPC_BOOT) + pr_ipc(0, tag, skb->data, len); +#endif +#if 0 + if (iod->format == IPC_RAMDUMP) + pr_ipc(0, tag, skb->data, len); +#endif +#if 0 + if (ch == 28) + pr_ipc(0, tag, skb->data, len); +#endif +#endif /*DEBUG_MODEM_IF*/ + if (atomic_read(&iod->opened) <= 0) { - mif_info("%s: ERR! %s is not opened\n", link, iod->name); + mif_err("%s: ERR! %s is not opened\n", ld->name, iod->name); return -ENODEV; } if (ch >= SIPC5_CH_ID_RFS_0) - return rx_rfs_frame(skb); + return rx_raw_misc(skb); else if (ch >= SIPC5_CH_ID_FMT_0) return rx_fmt_frame(skb); else if (iod->io_typ == IODEV_MISC) @@ -513,342 +399,337 @@ static int rx_demux(struct link_device *ld, struct sk_buff *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) +/** + * rx_frame_config + * @iod: pointer to an instance of io_device structure + * @ld: pointer to an instance of link_device structure + * @buff: pointer to a buffer in which incoming data is stored + * @size: size of data in the buffer + * @frm: pointer to an instance of sipc5_frame_data structure + * + * 1) Checks a config field + * 2) Calculates the length of link layer header in an incoming frame and stores + * the value to "frm->hdr_len" + * 3) Stores the config field to "frm->hdr" and add the size of config field to + * "frm->hdr_rcvd" + * + * Returns the length of a config field that was copied to "frm" + */ +static int rx_frame_config(struct io_device *iod, struct link_device *ld, + u8 *buff, int 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); - } + int rest; + int rcvd; - 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; + if (unlikely(!sipc5_start_valid(buff))) { + mif_err("%s->%s: ERR! INVALID config 0x%02x\n", + ld->name, iod->name, buff[0]); + return -EBADMSG; } - 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->hdr_len = sipc5_get_hdr_len(buff); - frm->data_rcvd += len; + /* Calculate the size of a segment that will be copied */ + rest = frm->hdr_len; + rcvd = SIPC5_CONFIG_SIZE; + mif_debug("%s->%s: hdr_len:%d hdr_rcvd:%d rest:%d size:%d rcvd:%d\n", + ld->name, iod->name, frm->hdr_len, frm->hdr_rcvd, rest, size, + rcvd); - mif_debug("%s: FRM data_len:%d, data_rcvd:%d\n", - link, frm->data_len, frm->data_rcvd); + /* Copy the config field of an SIPC5 link header to the header buffer */ + memcpy(frm->hdr, buff, rcvd); + frm->hdr_rcvd += rcvd; - return len; + return rcvd; } -static int rx_frame_from_serial(struct io_device *iod, struct link_device *ld, - const char *data, unsigned size) +/** + * rx_frame_prepare_skb + * @iod: pointer to an instance of io_device structure + * @ld: pointer to an instance of link_device structure + * @frm: pointer to an instance of sipc5_frame_data structure + * + * 1) Extracts the length of a link frame from the link header in "frm->hdr" + * 2) Allocates an skb + * 3) Calculates the payload size in the link frame + * 4) Calculates the padding size in the link frame + * + * Returns the pointer to an skb + */ +static struct sk_buff *rx_frame_prepare_skb(struct io_device *iod, + struct link_device *ld, struct sipc5_frame_data *frm) { - 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", link, size); + /* Get the frame length */ + frm->len = sipc5_get_frame_len(frm->hdr); - if (frm->hdr_rcvd >= frm->hdr_len && frm->data_rcvd < frm->data_len) { - /* - ** 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", - link, frm->data_len, frm->data_rcvd); - goto recv_data; + /* Allocate an skb */ + skb = rx_alloc_skb(frm->len, iod, ld); + if (!skb) { + mif_err("%s->%s: ERR! rx_alloc_skb fail (size %d)\n", + ld->name, iod->name, frm->len); + return NULL; } -next_frame: - /* Receive and analyze header, then prepare an akb */ - err = done = rx_header_from_serial(iod, ld, buff, rest, frm); - if (err < 0) - goto err_exit; - - buff += done; - rest -= done; - mif_debug("%s: rx_header() -> done:%d rest:%d\n", link, done, rest); - if (rest < 0) - goto err_range; + /* Calculates the payload size */ + frm->pay_len = frm->len - frm->hdr_len; - if (rest == 0) - return size; + /* Calculates the padding size */ + if (sipc5_padding_exist(frm->hdr)) + frm->pad_len = sipc5_calc_padding_size(frm->len); -recv_data: - err = 0; + mif_debug("%s->%s: size %d (header:%d payload:%d padding:%d)\n", + ld->name, iod->name, frm->len, frm->hdr_len, frm->pay_len, + frm->pad_len); - mif_debug("%s: done:%d rest:%d -> rx_payload()\n", link, done, rest); - - done = rx_payload_from_serial(iod, ld, buff, rest, frm); - buff += done; - rest -= done; + return skb; +} - mif_debug("%s: rx_payload() -> done:%d rest:%d\n", link, done, rest); +/** + * rx_frame_header + * @iod: pointer to an instance of io_device structure + * @ld: pointer to an instance of link_device structure + * @buff: pointer to a buffer in which incoming data is stored + * @size: size of data in the buffer + * @frm: pointer to an instance of sipc5_frame_data structure + * + * 1) Stores a link layer header to "frm->hdr" temporarily while "frm->hdr_rcvd" + * is less than "frm->hdr_len" + * 2) Then, + * Allocates an skb + * Copies the link header from "frm" to "skb" + * Register the skb to receive payload + * + * Returns the size of a segment that was copied to "frm" + */ +static int rx_frame_header(struct io_device *iod, struct link_device *ld, + u8 *buff, int size, struct sipc5_frame_data *frm) +{ + struct sk_buff *skb; + int rest; + int rcvd; - if (rest == 0 && frm->data_rcvd < frm->data_len) { - /* - Data is being received and more data will come within the next - frame from the link device. - */ - return size; - } + /* Calculate the size of a segment that will be copied */ + rest = frm->hdr_len - frm->hdr_rcvd; + rcvd = min(rest, size); + mif_debug("%s->%s: hdr_len:%d hdr_rcvd:%d rest:%d size:%d rcvd:%d\n", + ld->name, iod->name, frm->hdr_len, frm->hdr_rcvd, rest, size, + rcvd); - /* At this point, one complete link layer frame has been received. */ + /* Copy a segment of an SIPC5 link header to "frm" */ + memcpy((frm->hdr + frm->hdr_rcvd), buff, rcvd); + frm->hdr_rcvd += rcvd; - /* A padding size is applied to access the next IPC frame. */ - if (frm->padding) { - done = sipc5_calc_padding_size(frm->len); - if (done > rest) { - mif_info("%s: ERR! padding %d > rest %d\n", - link, done, rest); - goto err_exit; + if (frm->hdr_rcvd >= frm->hdr_len) { + /* Prepare an skb with the information in {iod, ld, frm} */ + skb = rx_frame_prepare_skb(iod, ld, frm); + if (!skb) { + mif_err("%s->%s: ERR! rx_frame_prepare_skb fail\n", + ld->name, iod->name); + return -ENOMEM; } - buff += done; - rest -= done; - - mif_debug("%s: padding:%d -> rest:%d\n", link, done, rest); - - if (rest < 0) - goto err_range; - - } - - skb = fragdata(iod, ld)->skb_recv; - if (likely(skb)) { - 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", link, skb->len); - } - - /* initialize the skb_recv and the frame_data buffer */ - fragdata(iod, ld)->skb_recv = NULL; - memset(frm, 0, sizeof(struct sipc5_frame_data)); + /* Copy an SIPC5 link header from "frm" to "skb" */ + memcpy(skb_put(skb, frm->hdr_len), frm->hdr, frm->hdr_len); - if (rest > 0) - goto next_frame; - - if (rest <= 0) - return size; - -err_exit: - if (fragdata(iod, ld)->skb_recv && - frm->hdr_rcvd >= frm->hdr_len && frm->data_rcvd >= frm->data_len) { - 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", link); + /* Register the skb to receive payload */ + fragdata(iod, ld)->skb_recv = skb; } - return err; -err_range: - mif_info("%s: ERR! size:%d vs. rest:%d\n", link, size, rest); - return size; + return rcvd; } /** - * 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 + * rx_frame_payload + * @iod: pointer to an instance of io_device structure + * @ld: pointer to an instance of link_device structure + * @buff: pointer to a buffer in which incoming data is stored + * @size: size of data in the buffer + * @frm: pointer to an instance of sipc5_frame_data structure * - * 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 + * Stores a link layer payload to "skb" * - * Returns SIPC5 header length + * Returns the size of a segment that was copied to "skb" */ -static int rx_header_from_mem(struct link_device *ld, u8 *buff, unsigned rest, - struct sipc5_frame_data *frm) +static int rx_frame_payload(struct io_device *iod, struct link_device *ld, + u8 *buff, int size, struct sipc5_frame_data *frm) { - char *link = ld->name; - u8 cfg = buff[0]; + struct sk_buff *skb = fragdata(iod, ld)->skb_recv; + int rest; + int rcvd; - /* 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); + /* Calculate the size of a segment that will be copied */ + rest = frm->pay_len - frm->pay_rcvd; + rcvd = min(rest, size); + mif_debug("%s->%s: pay_len:%d pay_rcvd:%d rest:%d size:%d rcvd:%d\n", + ld->name, iod->name, frm->pay_len, frm->pay_rcvd, rest, size, + rcvd); - /* Store the link layer header to the header buffer */ - memcpy(frm->hdr, buff, frm->hdr_len); - frm->hdr_rcvd = frm->hdr_len; + /* Copy an SIPC5 link payload to "skb" */ + memcpy(skb_put(skb, rcvd), buff, rcvd); + frm->pay_rcvd += rcvd; - /* Build and store the meta data of this frame */ - rx_build_meta_data(ld, frm); + return rcvd; +} - /* Verify frame length */ - if (unlikely(frm->len > rest)) { - mif_info("%s: ERR! frame length %d > rest %d\n", - link, frm->len, rest); - return -EBADMSG; - } +static int rx_frame_padding(struct io_device *iod, struct link_device *ld, + u8 *buff, int size, struct sipc5_frame_data *frm) +{ + struct sk_buff *skb = fragdata(iod, ld)->skb_recv; + int rest; + int rcvd; + + /* Calculate the size of a segment that will be dropped as padding */ + rest = frm->pad_len - frm->pad_rcvd; + rcvd = min(rest, size); + mif_debug("%s->%s: pad_len:%d pad_rcvd:%d rest:%d size:%d rcvd:%d\n", + ld->name, iod->name, frm->pad_len, frm->pad_rcvd, rest, size, + rcvd); - return frm->hdr_rcvd; + /* Copy an SIPC5 link padding to "skb" */ + memcpy(skb_put(skb, rcvd), buff, rcvd); + frm->pad_rcvd += rcvd; + + return rcvd; } -/* copy data to skb */ -static int rx_payload_from_mem(struct sk_buff *skb, u8 *buff, unsigned len) +static int rx_frame_done(struct io_device *iod, struct link_device *ld, + struct sk_buff *skb) { - /* If there is no skb, data must be dropped. */ - if (skb) - memcpy(skb_put(skb, len), buff, len); - return len; + /* Cut off the padding of the current frame */ + skb_trim(skb, sipc5_get_frame_len(skb->data)); + mif_debug("%s->%s: frame length = %d\n", ld->name, iod->name, skb->len); + + return rx_demux(ld, skb); } -static int rx_frame_from_mem(struct io_device *iod, struct link_device *ld, +static int recv_frame_from_buff(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; + int done = 0; + int err = 0; - mif_debug("%s: size = %d\n", link, size); + mif_debug("%s->%s: size %d (RX state = %s)\n", ld->name, iod->name, + size, get_rx_state_str(iod->curr_rx_state)); while (rest > 0) { - /* Initialize the frame data buffer */ - memset(frm, 0, sizeof(struct sipc5_frame_data)); - skb = NULL; + switch (iod->curr_rx_state) { + case IOD_RX_ON_STANDBY: + fragdata(iod, ld)->skb_recv = NULL; + memset(frm, 0, sizeof(struct sipc5_frame_data)); + + done = rx_frame_config(iod, ld, buff, rest, frm); + if (done < 0) { + err = done; + goto err_exit; + } - /* Receive and analyze link layer header */ - done = rx_header_from_mem(ld, buff, rest, frm); - if (unlikely(done < 0)) - return -EBADMSG; + iod->next_rx_state = IOD_RX_HEADER; - /* Verify rest size */ - rest -= done; - if (rest < 0) { - mif_info("%s: ERR! rx_header -> rest %d\n", link, rest); - return -ERANGE; - } + break; - /* Move buff pointer to the payload */ - buff += done; + case IOD_RX_HEADER: + done = rx_frame_header(iod, ld, buff, rest, frm); + if (done < 0) { + err = done; + goto err_exit; + } - /* Prepare an akb */ - len = frm->data_len; - skb = rx_alloc_skb(len, iod, ld); + if (frm->hdr_rcvd >= frm->hdr_len) + iod->next_rx_state = IOD_RX_PAYLOAD; + else + iod->next_rx_state = IOD_RX_HEADER; - /* Store channel ID and control fields to the CB of the skb */ - skbpriv(skb)->ch_id = frm->ch_id; - skbpriv(skb)->control = frm->control; + break; - /* Receive payload */ - 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) { - mif_info("%s: ERR! rx_payload() -> rest %d\n", - link, rest); - if (skb) - dev_kfree_skb_any(skb); - return -ERANGE; - } - buff += done; + case IOD_RX_PAYLOAD: + done = rx_frame_payload(iod, ld, buff, rest, frm); + if (done < 0) { + err = done; + goto err_exit; + } - /* A padding size is applied to access the next IPC frame. */ - if (frm->padding) { - done = sipc5_calc_padding_size(frm->len); - if (done > rest) { - mif_info("%s: ERR! padding %d > rest %d\n", - link, done, rest); - if (skb) - dev_kfree_skb_any(skb); - return -ERANGE; + if (frm->pay_rcvd >= frm->pay_len) { + if (frm->pad_len > 0) + iod->next_rx_state = IOD_RX_PADDING; + else + iod->next_rx_state = IOD_RX_ON_STANDBY; + } else { + iod->next_rx_state = IOD_RX_PAYLOAD; } - buff += done; - rest -= done; + + break; + + case IOD_RX_PADDING: + done = rx_frame_padding(iod, ld, buff, rest, frm); + if (done < 0) { + err = done; + goto err_exit; + } + + if (frm->pad_rcvd >= frm->pad_len) + iod->next_rx_state = IOD_RX_ON_STANDBY; + else + iod->next_rx_state = IOD_RX_PADDING; + + break; + + default: + mif_err("%s->%s: ERR! INVALID RX state %d\n", + ld->name, iod->name, iod->curr_rx_state); + err = -EINVAL; + goto err_exit; } - if (likely(skb)) { - 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", link, skb->len); + if (iod->next_rx_state == IOD_RX_ON_STANDBY) { + /* + ** A complete frame is in fragdata(iod, ld)->skb_recv. + */ + skb = fragdata(iod, ld)->skb_recv; + err = rx_frame_done(iod, ld, skb); + if (err < 0) + goto err_exit; } + + buff += done; + rest -= done; + if (rest < 0) + goto err_range; + + iod->curr_rx_state = iod->next_rx_state; } - return 0; + return size; + +err_exit: + if (fragdata(iod, ld)->skb_recv) { + mif_err("%s->%s: ERR! clear frag (size:%d done:%d rest:%d)\n", + ld->name, iod->name, size, done, rest); + dev_kfree_skb_any(fragdata(iod, ld)->skb_recv); + fragdata(iod, ld)->skb_recv = NULL; + } + iod->curr_rx_state = IOD_RX_ON_STANDBY; + return err; + +err_range: + mif_err("%s->%s: ERR! size:%d done:%d rest:%d\n", + ld->name, iod->name, size, done, rest); + iod->curr_rx_state = IOD_RX_ON_STANDBY; + return size; } /* called from link device when a packet arrives for this io device */ static int io_dev_recv_data_from_link_dev(struct io_device *iod, struct link_device *ld, const char *data, unsigned int len) { - struct sk_buff_head *rxq = &iod->sk_rx_q; struct sk_buff *skb; - char *link = ld->name; int err; - if (!data) { - mif_info("%s: ERR! !data\n", link); - return -EINVAL; - } - - if (len <= 0) { - mif_info("%s: ERR! len %d <= 0\n", link, len); - return -EINVAL; - } - switch (iod->format) { case IPC_FMT: case IPC_RAW: @@ -857,97 +738,151 @@ static int io_dev_recv_data_from_link_dev(struct io_device *iod, if (iod->waketime) wake_lock_timeout(&iod->wakelock, iod->waketime); - if (ld->link_type == LINKDEV_DPRAM && ld->aligned) - err = rx_frame_from_mem(iod, ld, data, len); - else - err = rx_frame_from_serial(iod, ld, data, len); - - if (err < 0) - mif_info("%s: ERR! rx_frame_from_link fail (err %d)\n", - link, err); + err = recv_frame_from_buff(iod, ld, data, len); + if (err < 0) { + mif_err("%s->%s: ERR! recv_frame_from_buff fail " + "(err %d)\n", ld->name, iod->name, err); + } return err; - case IPC_CMD: - case IPC_BOOT: - case IPC_RAMDUMP: + default: + mif_debug("%s->%s: len %d\n", ld->name, iod->name, len); + /* save packet to sk_buff */ skb = rx_alloc_skb(len, iod, ld); if (!skb) { - mif_info("%s: ERR! rx_alloc_skb fail\n", link); + mif_info("%s->%s: ERR! rx_alloc_skb fail\n", + ld->name, iod->name); return -ENOMEM; } - mif_debug("%s: len:%d -> iod:%s\n", link, len, iod->name); - memcpy(skb_put(skb, len), data, len); - skb_queue_tail(rxq, skb); - if (unlikely(rxq->qlen > 2048)) { - struct sk_buff *victim; - mif_info("%s: ERR! rxq->qlen %d > 2048\n", - iod->name, rxq->qlen); - victim = skb_dequeue(rxq); - dev_kfree_skb_any(victim); - } + + queue_skb_to_iod(skb, iod); + wake_up(&iod->wq); return len; - - default: - mif_info("%s: ERR! unknown format %d\n", link, iod->format); - return -EINVAL; } } -static int rx_frame_from_skb(struct io_device *iod, struct link_device *ld, +static int recv_frame_from_skb(struct io_device *iod, struct link_device *ld, struct sk_buff *skb) { - struct sipc5_frame_data *frm = &fragdata(iod, ld)->f_data; - u8 cfg = skb->data[0]; + struct sk_buff *clone; + unsigned int rest; + unsigned int rcvd; + int tot; /* total length including padding */ + int err = 0; - /* Initialize the frame data buffer */ - memset(frm, 0, sizeof(struct sipc5_frame_data)); + /* + ** If there is only one SIPC5 frame in @skb, receive the SIPC5 frame and + ** return immediately. In this case, the frame verification must already + ** have been done at the link device. + */ + if (skbpriv(skb)->single_frame) { + err = rx_frame_done(iod, ld, skb); + if (err < 0) + goto exit; + return 0; + } /* - ** The start of a link layer header has already been checked in the - ** link device. + ** The routine from here is used only if there may be multiple SIPC5 + ** frames in @skb. */ - /* Analyze the configuration of the link layer header */ - rx_check_frame_cfg(cfg, frm); + /* Check the config field of the first frame in @skb */ + if (!sipc5_start_valid(skb->data)) { + mif_err("%s->%s: ERR! INVALID config 0x%02X\n", + ld->name, iod->name, skb->data[0]); + err = -EINVAL; + goto exit; + } + + /* Get the total length of the frame with a padding */ + tot = sipc5_get_total_len(skb->data); - /* Store the link layer header to the header buffer */ - memcpy(frm->hdr, skb->data, frm->hdr_len); - frm->hdr_rcvd = frm->hdr_len; + /* Verify the total length of the first frame */ + rest = skb->len; + if (unlikely(tot > rest)) { + mif_err("%s->%s: ERR! tot %d > skb->len %d)\n", + ld->name, iod->name, tot, rest); + err = -EINVAL; + goto exit; + } - /* Build and store the meta data of this frame */ - rx_build_meta_data(ld, frm); + /* If there is only one SIPC5 frame in @skb, */ + if (likely(tot == rest)) { + /* Receive the SIPC5 frame and return immediately */ + err = rx_frame_done(iod, ld, skb); + if (err < 0) + goto exit; + return 0; + } /* - ** The length of the frame has already been checked in the link device. + ** This routine is used only if there are multiple SIPC5 frames in @skb. */ + rcvd = 0; + while (rest > 0) { + clone = skb_clone(skb, GFP_ATOMIC); + if (unlikely(!clone)) { + mif_err("%s->%s: ERR! skb_clone fail\n", + ld->name, iod->name); + err = -ENOMEM; + goto exit; + } - /* Trim the link layer header off the frame */ - skb_pull(skb, frm->hdr_len); + /* Get the start of an SIPC5 frame */ + skb_pull(clone, rcvd); + if (!sipc5_start_valid(clone->data)) { + mif_err("%s->%s: ERR! INVALID config 0x%02X\n", + ld->name, iod->name, clone->data[0]); + dev_kfree_skb_any(clone); + err = -EINVAL; + goto exit; + } - /* Store channel ID and control fields to the CB of the skb */ - skbpriv(skb)->ch_id = frm->ch_id; - skbpriv(skb)->control = frm->control; + /* Get the total length of the current frame with a padding */ + tot = sipc5_get_total_len(clone->data); + if (unlikely(tot > rest)) { + mif_err("%s->%s: ERR! dirty frame (tot %d > rest %d)\n", + ld->name, iod->name, tot, rest); + dev_kfree_skb_any(clone); + err = -EINVAL; + goto exit; + } - /* Demux the frame */ - if (rx_demux(ld, skb) < 0) { - mif_err("%s: ERR! rx_demux fail\n", ld->name); - return -EINVAL; + /* Cut off the padding of the current frame */ + skb_trim(clone, sipc5_get_frame_len(clone->data)); + + /* Demux the frame */ + err = rx_demux(ld, clone); + if (err < 0) { + mif_err("%s->%s: ERR! rx_demux fail (err %d)\n", + ld->name, iod->name, err); + dev_kfree_skb_any(clone); + goto exit; + } + + /* Calculate the start of the next frame */ + rcvd += tot; + + /* Calculate the rest size of data in @skb */ + rest -= tot; } - return 0; +exit: + dev_kfree_skb_any(skb); + return err; } /* 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; @@ -959,17 +894,35 @@ static int io_dev_recv_skb_from_link_dev(struct io_device *iod, if (iod->waketime) wake_lock_timeout(&iod->wakelock, iod->waketime); - err = rx_frame_from_skb(iod, ld, skb); + err = recv_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); + mif_err("%s->%s: ERR! recv_frame_from_skb fail " + "(err %d)\n", ld->name, iod->name, err); + } + + return err; + + case IPC_BOOT: + case IPC_RAMDUMP: + if (!iod->id) { + mif_err("%s->%s: ERR! invalid iod\n", + ld->name, iod->name); + return -ENODEV; + } + + if (iod->waketime) + wake_lock_timeout(&iod->wakelock, iod->waketime); + + err = recv_frame_from_skb(iod, ld, skb); + if (err < 0) { + mif_err("%s->%s: ERR! recv_frame_from_skb fail " + "(err %d)\n", ld->name, iod->name, err); } return err; default: - mif_info("%s: ERR! unknown device %d\n", link, dev); + mif_err("%s->%s: ERR! invalid iod\n", ld->name, iod->name); return -EINVAL; } } @@ -980,10 +933,14 @@ static int io_dev_recv_skb_from_link_dev(struct io_device *iod, static void io_dev_modem_state_changed(struct io_device *iod, enum modem_state state) { - mif_info("%s: %s state changed (state %d)\n", - iod->name, iod->mc->name, state); + struct modem_ctl *mc = iod->mc; + int old_state = mc->phone_state; - iod->mc->phone_state = state; + if (old_state != state) { + mc->phone_state = state; + mif_err("%s state changed (%s -> %s)\n", mc->name, + get_cp_state_str(old_state), get_cp_state_str(state)); + } if (state == STATE_CRASH_RESET || state == STATE_CRASH_EXIT || state == STATE_NV_REBUILDING) @@ -1024,23 +981,27 @@ static int misc_open(struct inode *inode, struct file *filp) struct io_device *iod = to_io_device(filp->private_data); struct modem_shared *msd = iod->msd; struct link_device *ld; + int ref_cnt; int ret; filp->private_data = (void *)iod; - atomic_inc(&iod->opened); - 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) { - mif_info("%s: init_comm fail(%d)\n", - ld->name, ret); + mif_err("%s<->%s: ERR! init_comm fail(%d)\n", + iod->name, ld->name, ret); return ret; } } } - mif_err("%s (opened %d)\n", iod->name, atomic_read(&iod->opened)); + ref_cnt = atomic_inc_return(&iod->opened); + + if (iod->format == IPC_BOOT || iod->format == IPC_RAMDUMP) + mif_err("%s (opened %d)\n", iod->name, ref_cnt); + else + mif_info("%s (opened %d)\n", iod->name, ref_cnt); return 0; } @@ -1050,8 +1011,8 @@ static int misc_release(struct inode *inode, struct file *filp) struct io_device *iod = (struct io_device *)filp->private_data; struct modem_shared *msd = iod->msd; struct link_device *ld; + int ref_cnt; - atomic_dec(&iod->opened); skb_queue_purge(&iod->sk_rx_q); list_for_each_entry(ld, &msd->link_dev_list, list) { @@ -1059,7 +1020,12 @@ static int misc_release(struct inode *inode, struct file *filp) ld->terminate_comm(ld, iod); } - mif_err("%s (opened %d)\n", iod->name, atomic_read(&iod->opened)); + ref_cnt = atomic_dec_return(&iod->opened); + + if (iod->format == IPC_BOOT || iod->format == IPC_RAMDUMP) + mif_err("%s (opened %d)\n", iod->name, ref_cnt); + else + mif_info("%s (opened %d)\n", iod->name, ref_cnt); return 0; } @@ -1067,20 +1033,23 @@ static int misc_release(struct inode *inode, struct file *filp) static unsigned int misc_poll(struct file *filp, struct poll_table_struct *wait) { struct io_device *iod = (struct io_device *)filp->private_data; + struct modem_ctl *mc = iod->mc; poll_wait(filp, &iod->wq, wait); - if (!skb_queue_empty(&iod->sk_rx_q) && - iod->mc->phone_state != STATE_OFFLINE) { + if (!skb_queue_empty(&iod->sk_rx_q) && mc->phone_state != STATE_OFFLINE) return POLLIN | POLLRDNORM; - } else if ((iod->mc->phone_state == STATE_CRASH_RESET) || - (iod->mc->phone_state == STATE_CRASH_EXIT) || - (iod->mc->phone_state == STATE_NV_REBUILDING) || - (iod->mc->sim_state.changed)) { + + if (mc->phone_state == STATE_CRASH_RESET + || mc->phone_state == STATE_CRASH_EXIT + || mc->phone_state == STATE_NV_REBUILDING + || mc->sim_state.changed) { if (iod->format == IPC_RAW) { msleep(20); return 0; } + if (iod->format == IPC_RAMDUMP) + return 0; return POLLHUP; } else { return 0; @@ -1089,132 +1058,191 @@ static unsigned int misc_poll(struct file *filp, struct poll_table_struct *wait) static long misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { - int p_state; struct io_device *iod = (struct io_device *)filp->private_data; struct link_device *ld = get_current_link(iod); - char cpinfo_buf[530] = "CP Crash "; + struct modem_ctl *mc = iod->mc; + int p_state; + char *buff; + void __user *user_buff; unsigned long size; - int ret; switch (cmd) { case IOCTL_MODEM_ON: - mif_info("%s: IOCTL_MODEM_ON\n", iod->name); - return iod->mc->ops.modem_on(iod->mc); + if (mc->ops.modem_on) { + mif_err("%s: IOCTL_MODEM_ON\n", iod->name); + return mc->ops.modem_on(mc); + } + mif_err("%s: !mc->ops.modem_on\n", iod->name); + return -EINVAL; case IOCTL_MODEM_OFF: - mif_info("%s: IOCTL_MODEM_OFF\n", iod->name); - return iod->mc->ops.modem_off(iod->mc); + if (mc->ops.modem_off) { + mif_err("%s: IOCTL_MODEM_OFF\n", iod->name); + return mc->ops.modem_off(mc); + } + mif_err("%s: !mc->ops.modem_off\n", iod->name); + return -EINVAL; case IOCTL_MODEM_RESET: - mif_info("%s: IOCTL_MODEM_RESET\n", iod->name); - return iod->mc->ops.modem_reset(iod->mc); + if (mc->ops.modem_reset) { + mif_err("%s: IOCTL_MODEM_RESET\n", iod->name); + return mc->ops.modem_reset(mc); + } + mif_err("%s: !mc->ops.modem_reset\n", iod->name); + return -EINVAL; case IOCTL_MODEM_BOOT_ON: - mif_info("%s: IOCTL_MODEM_BOOT_ON\n", iod->name); - return iod->mc->ops.modem_boot_on(iod->mc); + if (mc->ops.modem_boot_on) { + mif_err("%s: IOCTL_MODEM_BOOT_ON\n", iod->name); + return mc->ops.modem_boot_on(mc); + } + mif_err("%s: !mc->ops.modem_boot_on\n", iod->name); + return -EINVAL; case IOCTL_MODEM_BOOT_OFF: - mif_info("%s: IOCTL_MODEM_BOOT_OFF\n", iod->name); - return iod->mc->ops.modem_boot_off(iod->mc); + if (mc->ops.modem_boot_off) { + mif_err("%s: IOCTL_MODEM_BOOT_OFF\n", iod->name); + return mc->ops.modem_boot_off(mc); + } + mif_err("%s: !mc->ops.modem_boot_off\n", iod->name); + return -EINVAL; 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; + if (mc->ops.modem_boot_done) + return mc->ops.modem_boot_done(mc); + return 0; case IOCTL_MODEM_STATUS: mif_debug("%s: IOCTL_MODEM_STATUS\n", iod->name); - p_state = iod->mc->phone_state; + p_state = mc->phone_state; if ((p_state == STATE_CRASH_RESET) || (p_state == STATE_CRASH_EXIT)) { - mif_info("%s: IOCTL_MODEM_STATUS (state %d)\n", - iod->name, p_state); - } else if (iod->mc->sim_state.changed) { - int s_state = iod->mc->sim_state.online ? + mif_info("%s: IOCTL_MODEM_STATUS (state %s)\n", + iod->name, get_cp_state_str(p_state)); + } else if (mc->sim_state.changed) { + int s_state = mc->sim_state.online ? STATE_SIM_ATTACH : STATE_SIM_DETACH; - iod->mc->sim_state.changed = false; + mc->sim_state.changed = false; return s_state; } else if (p_state == STATE_NV_REBUILDING) { - mif_info("%s: IOCTL_MODEM_STATUS (state %d)\n", - iod->name, p_state); - iod->mc->phone_state = STATE_ONLINE; + mif_info("%s: IOCTL_MODEM_STATUS (state %s)\n", + iod->name, get_cp_state_str(p_state)); + mc->phone_state = STATE_ONLINE; } return p_state; - case IOCTL_MODEM_PROTOCOL_SUSPEND: - mif_debug("%s: IOCTL_MODEM_PROTOCOL_SUSPEND\n", - iod->name); - - if (iod->format != IPC_MULTI_RAW) - return -EINVAL; + case IOCTL_MODEM_XMIT_BOOT: + if (ld->xmit_boot) { + mif_info("%s: IOCTL_MODEM_XMIT_BOOT\n", iod->name); + return ld->xmit_boot(ld, iod, arg); + } + mif_err("%s: !ld->xmit_boot\n", iod->name); + return -EINVAL; - iodevs_for_each(iod->msd, iodev_netif_stop, 0); - return 0; + case IOCTL_MODEM_DL_START: + if (ld->dload_start) { + mif_info("%s: IOCTL_MODEM_DL_START\n", iod->name); + return ld->dload_start(ld, iod); + } + mif_err("%s: !ld->dload_start\n", iod->name); + return -EINVAL; - case IOCTL_MODEM_PROTOCOL_RESUME: - mif_info("%s: IOCTL_MODEM_PROTOCOL_RESUME\n", - iod->name); + case IOCTL_MODEM_FW_UPDATE: + if (ld->firm_update) { + mif_info("%s: IOCTL_MODEM_FW_UPDATE\n", iod->name); + return ld->firm_update(ld, iod, arg); + } + mif_err("%s: !ld->firm_update\n", iod->name); + return -EINVAL; - if (iod->format != IPC_MULTI_RAW) - return -EINVAL; + case IOCTL_MODEM_FORCE_CRASH_EXIT: + if (mc->ops.modem_force_crash_exit) { + mif_err("%s: IOCTL_MODEM_FORCE_CRASH_EXIT\n", + iod->name); + return mc->ops.modem_force_crash_exit(mc); + } + mif_err("%s: !mc->ops.modem_force_crash_exit\n", iod->name); + return -EINVAL; - iodevs_for_each(iod->msd, iodev_netif_wake, 0); - return 0; + case IOCTL_MODEM_DUMP_RESET: + if (mc->ops.modem_dump_reset) { + mif_info("%s: IOCTL_MODEM_DUMP_RESET\n", iod->name); + return mc->ops.modem_dump_reset(mc); + } + mif_err("%s: !mc->ops.modem_dump_reset\n", iod->name); + return -EINVAL; case IOCTL_MODEM_DUMP_START: - mif_info("%s: IOCTL_MODEM_DUMP_START\n", iod->name); - return ld->dump_start(ld, iod); + if (ld->dump_start) { + mif_err("%s: IOCTL_MODEM_DUMP_START\n", iod->name); + return ld->dump_start(ld, iod); + } + mif_err("%s: !ld->dump_start\n", iod->name); + return -EINVAL; + + case IOCTL_MODEM_RAMDUMP_START: + if (ld->dump_start) { + mif_info("%s: IOCTL_MODEM_RAMDUMP_START\n", iod->name); + return ld->dump_start(ld, iod); + } + mif_err("%s: !ld->dump_start\n", iod->name); + return -EINVAL; case IOCTL_MODEM_DUMP_UPDATE: - mif_debug("%s: IOCTL_MODEM_DUMP_UPDATE\n", iod->name); - return ld->dump_update(ld, iod, arg); + if (ld->dump_update) { + mif_info("%s: IOCTL_MODEM_DUMP_UPDATE\n", iod->name); + return ld->dump_update(ld, iod, arg); + } + mif_err("%s: !ld->dump_update\n", iod->name); + return -EINVAL; - case IOCTL_MODEM_FORCE_CRASH_EXIT: - mif_info("%s: IOCTL_MODEM_FORCE_CRASH_EXIT\n", iod->name); - if (iod->mc->ops.modem_force_crash_exit) - return iod->mc->ops.modem_force_crash_exit(iod->mc); + case IOCTL_MODEM_RAMDUMP_STOP: + if (ld->dump_finish) { + mif_info("%s: IOCTL_MODEM_RAMDUMP_STOP\n", iod->name); + return ld->dump_finish(ld, iod, arg); + } + mif_err("%s: !ld->dump_finish\n", iod->name); return -EINVAL; case IOCTL_MODEM_CP_UPLOAD: mif_info("%s: IOCTL_MODEM_CP_UPLOAD\n", iod->name); - if (copy_from_user(cpinfo_buf + strlen(cpinfo_buf), - (void __user *)arg, MAX_CPINFO_SIZE) != 0) - return -EFAULT; - panic(cpinfo_buf); + strcpy(iod->msd->cp_crash_info, CP_CRASH_TAG); + if (arg) { + buff = iod->msd->cp_crash_info + strlen(CP_CRASH_TAG); + user_buff = (void __user *)arg; + if (copy_from_user(buff, user_buff, MAX_CPINFO_SIZE)) + return -EFAULT; + } + panic(iod->msd->cp_crash_info); return 0; - case IOCTL_MODEM_DUMP_RESET: - mif_info("%s: IOCTL_MODEM_DUMP_RESET\n", iod->name); - return iod->mc->ops.modem_dump_reset(iod->mc); + case IOCTL_MODEM_PROTOCOL_SUSPEND: + mif_info("%s: IOCTL_MODEM_PROTOCOL_SUSPEND\n", iod->name); + if (iod->format == IPC_MULTI_RAW) { + iodevs_for_each(iod->msd, iodev_netif_stop, 0); + return 0; + } + return -EINVAL; + + case IOCTL_MODEM_PROTOCOL_RESUME: + mif_info("%s: IOCTL_MODEM_PROTOCOL_RESUME\n", iod->name); + if (iod->format != IPC_MULTI_RAW) { + iodevs_for_each(iod->msd, iodev_netif_wake, 0); + return 0; + } + return -EINVAL; case IOCTL_MIF_LOG_DUMP: iodevs_for_each(iod->msd, iodev_dump_status, 0); + user_buff = (void __user *)arg; size = MAX_MIF_BUFF_SIZE; - ret = copy_to_user((void __user *)arg, &size, - sizeof(unsigned long)); - if (ret < 0) + if (copy_to_user(user_buff, &size, sizeof(unsigned long))) return -EFAULT; - - mif_dump_log(iod->mc->msd, iod); + mif_dump_log(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 @@ -1222,9 +1250,10 @@ static long misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) if (ld->ioctl) return ld->ioctl(ld, iod, cmd, arg); - mif_info("%s: ERR! cmd 0x%X not defined.\n", iod->name, cmd); + mif_info("%s: ERR! undefined cmd 0x%X\n", iod->name, cmd); return -EINVAL; } + return 0; } @@ -1234,49 +1263,76 @@ 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 sk_buff *skb; + u8 *buff; int ret; - unsigned headroom = 0; - unsigned tailroom = 0; - size_t tx_size; - struct sipc5_frame_data frm; - struct timespec epoch; + size_t headroom; + size_t tailroom; + size_t tx_bytes; + u8 cfg; if (iod->format <= IPC_RFS && iod->id == 0) return -EINVAL; - headroom = tx_build_link_header(&frm, iod, ld, count); + cfg = sipc5_build_config(iod, ld, count); + + if (cfg) + headroom = sipc5_get_hdr_len(&cfg); + else + headroom = 0; if (ld->aligned) tailroom = sipc5_calc_padding_size(headroom + count); + else + tailroom = 0; - tx_size = headroom + count + tailroom; + tx_bytes = headroom + count + tailroom; - skb = alloc_skb(tx_size, GFP_KERNEL); + skb = alloc_skb(tx_bytes, GFP_KERNEL); if (!skb) { - mif_info("%s: ERR! alloc_skb fail (tx_size:%d)\n", - iod->name, tx_size); + mif_info("%s: ERR! alloc_skb fail (tx_bytes:%d)\n", + iod->name, tx_bytes); return -ENOMEM; } - /* store IPC link header*/ - memcpy(skb_put(skb, headroom), frm.hdr, headroom); + /* Build SIPC5 link header*/ + if (cfg) { + buff = skb_put(skb, headroom); + sipc5_build_header(iod, ld, buff, cfg, 0, count); + } - /* store IPC message */ - if (copy_from_user(skb_put(skb, count), data, count) != 0) { - if (skb) - dev_kfree_skb_any(skb); + /* Store IPC message */ + buff = skb_put(skb, count); + if (copy_from_user(buff, data, count)) { + mif_err("%s->%s: ERR! copy_from_user fail (count %d)\n", + iod->name, ld->name, count); + dev_kfree_skb_any(skb); return -EFAULT; } - if (iod->id == SIPC5_CH_ID_FMT_0) { + /* Apply padding */ + if (tailroom) + skb_put(skb, tailroom); + + if (iod->format == IPC_FMT) { + struct timespec epoch; + u8 *msg = (skb->data + headroom); +#if 0 + char tag[MIF_MAX_STR_LEN]; + snprintf(tag, MIF_MAX_STR_LEN, "%s: RIL2MIF", iod->mc->name); + pr_ipc(1, tag, msg, (count > 20 ? 20 : count)); +#endif getnstimeofday(&epoch); mif_time_log(iod->mc->msd, epoch, NULL, 0); - mif_ipc_log(MIF_IPC_RL2AP, iod->mc->msd, skb->data, skb->len); + mif_ipc_log(MIF_IPC_RL2AP, iod->mc->msd, msg, count); } - /* store padding */ - if (tailroom) - skb_put(skb, tailroom); +#if 0 + if (iod->format == IPC_RAMDUMP) { + char tag[MIF_MAX_STR_LEN]; + snprintf(tag, MIF_MAX_STR_LEN, "%s: DUMP2MIF", iod->name); + pr_ipc(1, tag, skb->data, (skb->len > 20 ? 20 : skb->len)); + } +#endif /* send data with sk_buff, link device will put sk_buff * into the specific sk_buff_q and run work-q to send data @@ -1286,13 +1342,15 @@ static ssize_t misc_write(struct file *filp, const char __user *data, ret = ld->send(ld, iod, skb); if (ret < 0) { - mif_info("%s: ERR! ld->send fail (err %d)\n", iod->name, ret); + mif_info("%s->%s: ERR! ld->send fail (err %d, tx_bytes %d)\n", + iod->name, ld->name, ret, tx_bytes); return ret; } - if (ret != tx_size) - mif_info("%s: wrong tx size (count:%d tx_size:%d ret:%d)\n", - iod->name, count, tx_size, ret); + if (ret != tx_bytes) { + mif_info("%s->%s: WARNING! ret %d != tx_bytes %d (count %d)\n", + iod->name, ld->name, ret, tx_bytes, count); + } return count; } @@ -1304,24 +1362,38 @@ 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) { + if (skb_queue_empty(rxq)) { mif_info("%s: ERR! no data in rxq\n", iod->name); return 0; } - if (iod->id == SIPC5_CH_ID_FMT_0) { + skb = skb_dequeue(rxq); + + if (iod->format == IPC_FMT) { + struct timespec epoch; +#if 0 + char tag[MIF_MAX_STR_LEN]; + snprintf(tag, MIF_MAX_STR_LEN, "%s: MIF2RIL", iod->mc->name); + pr_ipc(0, tag, skb->data, (skb->len > 20 ? 20 : skb->len)); +#endif getnstimeofday(&epoch); mif_time_log(iod->mc->msd, epoch, NULL, 0); mif_ipc_log(MIF_IPC_AP2RL, iod->mc->msd, skb->data, skb->len); } +#if 0 + if (iod->format == IPC_RAMDUMP) { + char tag[MIF_MAX_STR_LEN]; + snprintf(tag, MIF_MAX_STR_LEN, "%s: MIF2DUMP", iod->name); + pr_ipc(1, tag, skb->data, (skb->len > 20 ? 20 : skb->len)); + } +#endif + copied = skb->len > count ? count : skb->len; if (copy_to_user(buf, skb->data, copied)) { - mif_info("%s: ERR! copy_to_user fail\n", iod->name); + mif_err("%s: ERR! copy_to_user fail\n", iod->name); dev_kfree_skb_any(skb); return -EFAULT; } @@ -1339,43 +1411,6 @@ static ssize_t misc_read(struct file *filp, char *buf, size_t count, return copied; } -#ifdef CONFIG_LINK_DEVICE_C2C -static int misc_mmap(struct file *filp, struct vm_area_struct *vma) -{ - int r = 0; - unsigned long size = 0; - unsigned long pfn = 0; - unsigned long offset = 0; - struct io_device *iod = (struct io_device *)filp->private_data; - - if (!vma) - return -EFAULT; - - size = vma->vm_end - vma->vm_start; - offset = vma->vm_pgoff << PAGE_SHIFT; - if (offset + size > (C2C_CP_RGN_SIZE + C2C_SH_RGN_SIZE)) { - mif_info("ERR: offset + size > C2C_CP_RGN_SIZE\n"); - return -EINVAL; - } - - /* Set the noncacheable property to the region */ - vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); - vma->vm_flags |= VM_RESERVED | VM_IO; - - pfn = __phys_to_pfn(C2C_CP_RGN_ADDR + offset); - r = remap_pfn_range(vma, vma->vm_start, pfn, size, vma->vm_page_prot); - if (r) { - mif_info("ERR: Failed in remap_pfn_range()!!!\n"); - return -EAGAIN; - } - - mif_info("%s: VA = 0x%08lx, offset = 0x%lx, size = %lu\n", - iod->name, vma->vm_start, offset, size); - - return 0; -} -#endif - static const struct file_operations misc_io_fops = { .owner = THIS_MODULE, .open = misc_open, @@ -1384,9 +1419,6 @@ static const struct file_operations misc_io_fops = { .unlocked_ioctl = misc_ioctl, .write = misc_write, .read = misc_read, -#ifdef CONFIG_LINK_DEVICE_C2C - .mmap = misc_mmap, -#endif }; static int vnet_open(struct net_device *ndev) @@ -1419,11 +1451,13 @@ static int vnet_xmit(struct sk_buff *skb, struct net_device *ndev) struct link_device *ld = get_current_link(iod); struct sk_buff *skb_new; int ret; - unsigned headroom = 0; - unsigned tailroom = 0; - unsigned long tx_bytes = skb->len; + unsigned headroom; + unsigned tailroom; + size_t count; + size_t tx_bytes; struct iphdr *ip_header = NULL; - struct sipc5_frame_data frm; + u8 *buff; + u8 cfg; /* When use `handover' with Network Bridge, * user -> bridge device(rmnet0) -> real rmnet(xxxx_rmnet0) -> here. @@ -1436,19 +1470,18 @@ static int vnet_xmit(struct sk_buff *skb, struct net_device *ndev) skb_pull(skb, sizeof(struct ethhdr)); } - headroom = tx_build_link_header(&frm, iod, ld, skb->len); + count = 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; - } + cfg = sipc5_build_config(iod, ld, count); + + headroom = sipc5_get_hdr_len(&cfg); if (ld->aligned) - tailroom = sipc5_calc_padding_size(frm.len); + tailroom = sipc5_calc_padding_size(headroom + count); + else + tailroom = 0; + + tx_bytes = headroom + count + tailroom; if (skb_headroom(skb) < headroom || skb_tailroom(skb) < tailroom) { mif_debug("%s: skb_copy_expand needed\n", iod->name); @@ -1463,7 +1496,18 @@ static int vnet_xmit(struct sk_buff *skb, struct net_device *ndev) skb_new = skb; } - memcpy(skb_push(skb_new, headroom), frm.hdr, headroom); + /* Build SIPC5 link header*/ + buff = skb_push(skb_new, headroom); + sipc5_build_header(iod, ld, buff, cfg, 0, count); + + /* 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); + buff[SIPC5_CH_ID_OFFSET] = DATA_LOOPBACK_CHANNEL; + } + if (tailroom) skb_put(skb_new, tailroom); @@ -1473,12 +1517,18 @@ static int vnet_xmit(struct sk_buff *skb, struct net_device *ndev) ret = ld->send(ld, iod, skb_new); if (ret < 0) { netif_stop_queue(ndev); - mif_info("%s: ERR! ld->send fail (err %d)\n", iod->name, ret); + mif_info("%s->%s: ERR! ld->send fail (err %d, tx_bytes %d)\n", + iod->name, ld->name, ret, tx_bytes); return NETDEV_TX_BUSY; } + if (ret != tx_bytes) { + mif_info("%s->%s: WARNING! ret %d != tx_bytes %d (count %d)\n", + iod->name, ld->name, ret, tx_bytes, count); + } + ndev->stats.tx_packets++; - ndev->stats.tx_bytes += tx_bytes; + ndev->stats.tx_bytes += count; return NETDEV_TX_OK; } @@ -1581,16 +1631,19 @@ int sipc5_init_io_device(struct io_device *iod) ret = misc_register(&iod->miscdev); if (ret) mif_info("%s: ERR! misc_register fail\n", iod->name); + ret = device_create_file(iod->miscdev.this_device, &attr_waketime); 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) diff --git a/drivers/misc/modem_if/sipc5_modem.c b/drivers/misc/modem_if/sipc5_modem.c index f5e33d3..8a173d4 100644 --- a/drivers/misc/modem_if/sipc5_modem.c +++ b/drivers/misc/modem_if/sipc5_modem.c @@ -33,7 +33,8 @@ #include <linux/delay.h> #include <linux/wakelock.h> -#include <linux/platform_data/modem.h> +#include "modem.h" +#include <mach/c2c.h> #include "modem_prj.h" #include "modem_variation.h" #include "modem_utils.h" @@ -77,27 +78,31 @@ static struct modem_shared *create_modem_shared_data(void) static struct modem_ctl *create_modemctl_device(struct platform_device *pdev, struct modem_shared *msd) { - int ret = 0; - struct modem_data *pdata; + struct modem_data *pdata = pdev->dev.platform_data; struct modem_ctl *modemctl; - struct device *dev = &pdev->dev; + int ret; /* create modem control device */ modemctl = kzalloc(sizeof(struct modem_ctl), GFP_KERNEL); - if (!modemctl) + if (!modemctl) { + mif_err("%s: modemctl kzalloc fail\n", pdata->name); + mif_err("%s: xxx\n", pdata->name); return NULL; + } modemctl->msd = msd; - modemctl->dev = dev; + modemctl->dev = &pdev->dev; modemctl->phone_state = STATE_OFFLINE; - pdata = pdev->dev.platform_data; modemctl->mdm_data = pdata; modemctl->name = pdata->name; /* init modemctl device for getting modemctl operations */ ret = call_modem_init_func(modemctl, pdata); if (ret) { + mif_err("%s: call_modem_init_func fail (err %d)\n", + pdata->name, ret); + mif_err("%s: xxx\n", pdata->name); kfree(modemctl); return NULL; } @@ -111,8 +116,8 @@ static struct io_device *create_io_device(struct modem_io_t *io_t, struct modem_shared *msd, struct modem_ctl *modemctl, struct modem_data *pdata) { - int ret = 0; - struct io_device *iod = NULL; + int ret; + struct io_device *iod; iod = kzalloc(sizeof(struct io_device), GFP_KERNEL); if (!iod) { @@ -128,6 +133,7 @@ static struct io_device *create_io_device(struct modem_io_t *io_t, iod->format = io_t->format; iod->io_typ = io_t->io_type; iod->link_types = io_t->links; + iod->app = io_t->app; iod->net_typ = pdata->modem_net; iod->use_handover = pdata->use_handover; iod->ipc_version = pdata->ipc_version; @@ -139,7 +145,7 @@ static struct io_device *create_io_device(struct modem_io_t *io_t, modemctl->iod = iod; if (iod->format == IPC_BOOT) { modemctl->bootd = iod; - mif_info("Bood device = %s\n", iod->name); + mif_err("BOOT device = %s\n", iod->name); } /* link between io device and modem shared */ @@ -160,7 +166,7 @@ static struct io_device *create_io_device(struct modem_io_t *io_t, return NULL; } - mif_debug("%s is created!!!\n", iod->name); + mif_info("%s created\n", iod->name); return iod; } @@ -168,7 +174,6 @@ 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; /* find link type for this io device */ list_for_each_entry(ld, &msd->link_dev_list, list) { @@ -232,36 +237,36 @@ static int __devinit modem_probe(struct platform_device *pdev) { int i; struct modem_data *pdata = pdev->dev.platform_data; - struct modem_shared *msd = NULL; - struct modem_ctl *modemctl = NULL; + struct modem_shared *msd; + struct modem_ctl *modemctl; struct io_device *iod[pdata->num_iodevs]; struct link_device *ld; - - mif_err("%s\n", pdev->name); - memset(iod, 0, sizeof(iod)); + mif_err("%s: +++\n", pdata->name); msd = create_modem_shared_data(); if (!msd) { - mif_err("msd == NULL\n"); - goto err_free_modemctl; + mif_err("%s: msd == NULL\n", pdata->name); + return -ENOMEM; } modemctl = create_modemctl_device(pdev, msd); if (!modemctl) { - mif_err("modemctl == NULL\n"); - goto err_free_modemctl; + mif_err("%s: modemctl == NULL\n", pdata->name); + kfree(msd); + return -ENOMEM; } /* create link device */ /* support multi-link device */ + memset(iod, 0, sizeof(iod)); for (i = 0; i < LINKDEV_MAX ; i++) { /* find matching link type */ if (pdata->link_types & LINKTYPE(i)) { ld = call_link_init_func(pdev, i); if (!ld) - goto err_free_modemctl; + goto free_mc; - mif_err("link created: %s\n", ld->name); + mif_err("%s: %s link created\n", pdata->name, ld->name); ld->link_type = i; ld->mc = modemctl; ld->msd = msd; @@ -274,8 +279,8 @@ static int __devinit modem_probe(struct platform_device *pdev) 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; + mif_err("%s: iod[%d] == NULL\n", pdata->name, i); + goto free_iod; } attach_devices(iod[i], pdata->iodevs[i].tx_link); @@ -283,21 +288,23 @@ static int __devinit modem_probe(struct platform_device *pdev) platform_set_drvdata(pdev, modemctl); - mif_err("Complete!!!\n"); - + mif_err("%s: ---\n", pdata->name); return 0; -err_free_modemctl: - for (i = 0; i < pdata->num_iodevs; i++) - if (iod[i] != NULL) +free_iod: + for (i = 0; i < pdata->num_iodevs; i++) { + if (iod[i]) kfree(iod[i]); + } - if (modemctl != NULL) +free_mc: + if (modemctl) kfree(modemctl); - if (msd != NULL) + if (msd) kfree(msd); + mif_err("%s: xxx\n", pdata->name); return -ENOMEM; } @@ -305,13 +312,19 @@ static void modem_shutdown(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct modem_ctl *mc = dev_get_drvdata(dev); + struct utc_time utc; + mc->ops.modem_off(mc); mc->phone_state = STATE_OFFLINE; + + get_utc_time(&utc); + mif_info("%s: at [%02d:%02d:%02d.%03d]\n", + mc->name, utc.hour, utc.min, utc.sec, utc.msec); } static int modem_suspend(struct device *pdev) { -#ifndef CONFIG_LINK_DEVICE_HSIC +#if !defined(CONFIG_LINK_DEVICE_HSIC) struct modem_ctl *mc = dev_get_drvdata(pdev); if (mc->gpio_pda_active) @@ -323,7 +336,7 @@ static int modem_suspend(struct device *pdev) static int modem_resume(struct device *pdev) { -#ifndef CONFIG_LINK_DEVICE_HSIC +#if !defined(CONFIG_LINK_DEVICE_HSIC) struct modem_ctl *mc = dev_get_drvdata(pdev); if (mc->gpio_pda_active) @@ -334,8 +347,8 @@ static int modem_resume(struct device *pdev) } static const struct dev_pm_ops modem_pm_ops = { - .suspend = modem_suspend, - .resume = modem_resume, + .suspend = modem_suspend, + .resume = modem_resume, }; static struct platform_driver modem_driver = { @@ -343,7 +356,7 @@ static struct platform_driver modem_driver = { .shutdown = modem_shutdown, .driver = { .name = "mif_sipc5", - .pm = &modem_pm_ops, + .pm = &modem_pm_ops, }, }; |