aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/misc
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/misc')
-rw-r--r--drivers/misc/Kconfig21
-rw-r--r--drivers/misc/Makefile1
-rw-r--r--drivers/misc/modem_if/Kconfig21
-rw-r--r--drivers/misc/modem_if/Makefile32
-rw-r--r--drivers/misc/modem_if/lte_modem_bootloader.c2
-rw-r--r--drivers/misc/modem_if/modem.h435
-rw-r--r--drivers/misc/modem_if/modem_boot_device_spi.c279
-rw-r--r--drivers/misc/modem_if/modem_link_device_c2c.c2277
-rw-r--r--drivers/misc/modem_if/modem_link_device_c2c.h202
-rw-r--r--drivers/misc/modem_if/modem_link_device_dpram.c3184
-rw-r--r--drivers/misc/modem_if/modem_link_device_dpram.h455
-rw-r--r--drivers/misc/modem_if/modem_link_device_dpram_ext_op.c1619
-rw-r--r--drivers/misc/modem_if/modem_link_device_hsic.c33
-rw-r--r--drivers/misc/modem_if/modem_link_device_memory.c496
-rw-r--r--drivers/misc/modem_if/modem_link_device_memory.h409
-rw-r--r--drivers/misc/modem_if/modem_link_device_mipi.c2
-rw-r--r--drivers/misc/modem_if/modem_link_device_pld.c1464
-rw-r--r--drivers/misc/modem_if/modem_link_device_pld.h578
-rw-r--r--drivers/misc/modem_if/modem_link_device_pld_ext_op.c277
-rw-r--r--drivers/misc/modem_if/modem_link_device_shmem.h700
-rw-r--r--drivers/misc/modem_if/modem_link_device_spi.c246
-rw-r--r--drivers/misc/modem_if/modem_link_device_spi.h24
-rw-r--r--drivers/misc/modem_if/modem_link_device_usb.c16
-rw-r--r--drivers/misc/modem_if/modem_link_pm_usb.c29
-rw-r--r--drivers/misc/modem_if/modem_link_pm_usb.h2
-rw-r--r--drivers/misc/modem_if/modem_modemctl_device_cbp71.c2
-rw-r--r--drivers/misc/modem_if/modem_modemctl_device_cbp72.c45
-rw-r--r--drivers/misc/modem_if/modem_modemctl_device_cbp82.c288
-rw-r--r--drivers/misc/modem_if/modem_modemctl_device_cmc221.c105
-rw-r--r--drivers/misc/modem_if/modem_modemctl_device_esc6270.c18
-rw-r--r--drivers/misc/modem_if/modem_modemctl_device_mdm6600.c43
-rw-r--r--drivers/misc/modem_if/modem_modemctl_device_qsc6085.c218
-rw-r--r--drivers/misc/modem_if/modem_modemctl_device_sprd8803.c60
-rw-r--r--drivers/misc/modem_if/modem_modemctl_device_ss222.c312
-rw-r--r--drivers/misc/modem_if/modem_modemctl_device_xmm6260.c12
-rw-r--r--drivers/misc/modem_if/modem_modemctl_device_xmm6262.c92
-rw-r--r--drivers/misc/modem_if/modem_net_flowcontrol_device.c2
-rw-r--r--drivers/misc/modem_if/modem_prj.h514
-rw-r--r--drivers/misc/modem_if/modem_sim_slot_switch.c11
-rw-r--r--drivers/misc/modem_if/modem_utils.c671
-rw-r--r--drivers/misc/modem_if/modem_utils.h56
-rw-r--r--drivers/misc/modem_if/modem_variation.h106
-rw-r--r--drivers/misc/modem_if/sipc4_io_device.c311
-rw-r--r--drivers/misc/modem_if/sipc4_modem.c15
-rw-r--r--drivers/misc/modem_if/sipc5_common.c239
-rw-r--r--drivers/misc/modem_if/sipc5_io_device.c1561
-rw-r--r--drivers/misc/modem_if/sipc5_modem.c87
-rw-r--r--drivers/misc/pmem.c1386
-rw-r--r--drivers/misc/tzic.c1
49 files changed, 12437 insertions, 6522 deletions
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 0559caa..1daa392 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -61,27 +61,6 @@ config AD525X_DPOT_SPI
To compile this driver as a module, choose M here: the
module will be called ad525x_dpot-spi.
-config ANDROID_PMEM
- bool "Android pmem allocator"
- depends on MACH_U1 || MACH_PX || MACH_TRATS
- default n
-
-if ANDROID_PMEM
- comment "Reserved memory configurations"
-
-config ANDROID_PMEM_MEMSIZE_PMEM
- int "Memory size in kbytes for android surface using pmem"
- default "4096"
-
-config ANDROID_PMEM_MEMSIZE_PMEM_GPU1
- int "Memory size in kbytes for android surface using pmem_gpu1"
- default "10240"
-
-config ANDROID_PMEM_MEMSIZE_PMEM_CAM
- int "Memory size in kbytes for android camera using pmem_camera"
- default "4096"
-endif
-
config ATMEL_PWM
tristate "Atmel AT32/AT91 PWM support"
depends on AVR32 || ARCH_AT91SAM9263 || ARCH_AT91SAM9RL || ARCH_AT91CAP9
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 44e4884..8d3b193 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -19,7 +19,6 @@ obj-$(CONFIG_PHANTOM) += phantom.o
obj-$(CONFIG_SENSORS_BH1780) += bh1780gli.o
obj-$(CONFIG_SENSORS_BH1770) += bh1770glc.o
obj-$(CONFIG_SENSORS_APDS990X) += apds990x.o
-obj-$(CONFIG_ANDROID_PMEM) += pmem.o
obj-$(CONFIG_SGI_IOC4) += ioc4.o
obj-$(CONFIG_ENCLOSURE_SERVICES) += enclosure.o
obj-$(CONFIG_KGDB_TESTS) += kgdbts.o
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 *)&param, (void *)arg, sizeof(param));
+ retval = copy_from_user((void *)&param, (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 *)&param, (void *)arg, sizeof(param));
+ retval = copy_from_user((void *)&param, (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, &param);
+ retval = qc_data_upload(dpld, &param);
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 *)&param, (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 *)&param, (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, &param);
+ retval = _qc_data_upload(pld, &param);
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,
},
};
diff --git a/drivers/misc/pmem.c b/drivers/misc/pmem.c
deleted file mode 100644
index c7c9179..0000000
--- a/drivers/misc/pmem.c
+++ /dev/null
@@ -1,1386 +0,0 @@
-/* drivers/android/pmem.c
- *
- * Copyright (C) 2007 Google, Inc.
- *
- * 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/miscdevice.h>
-#include <linux/platform_device.h>
-#include <linux/fs.h>
-#include <linux/file.h>
-#include <linux/mm.h>
-#include <linux/list.h>
-#include <linux/mutex.h>
-#include <linux/debugfs.h>
-#include <linux/android_pmem.h>
-#include <linux/mempolicy.h>
-#include <linux/sched.h>
-#include <linux/vmalloc.h>
-#include <asm/io.h>
-#include <asm/uaccess.h>
-#include <asm/cacheflush.h>
-#if defined(CONFIG_S5P_MEM_CMA)
-#include <linux/cma.h>
-#endif
-
-#define PMEM_MAX_DEVICES 10
-#define PMEM_MAX_ORDER 128
-#define PMEM_MIN_ALLOC PAGE_SIZE
-
-#define PMEM_DEBUG 1
-
-/* indicates that a refernce to this file has been taken via get_pmem_file,
- * the file should not be released until put_pmem_file is called */
-#define PMEM_FLAGS_BUSY 0x1
-/* indicates that this is a suballocation of a larger master range */
-#define PMEM_FLAGS_CONNECTED 0x1 << 1
-/* indicates this is a master and not a sub allocation and that it is mmaped */
-#define PMEM_FLAGS_MASTERMAP 0x1 << 2
-/* submap and unsubmap flags indicate:
- * 00: subregion has never been mmaped
- * 10: subregion has been mmaped, reference to the mm was taken
- * 11: subretion has ben released, refernece to the mm still held
- * 01: subretion has been released, reference to the mm has been released
- */
-#define PMEM_FLAGS_SUBMAP 0x1 << 3
-#define PMEM_FLAGS_UNSUBMAP 0x1 << 4
-
-
-struct pmem_data {
- /* in alloc mode: an index into the bitmap
- * in no_alloc mode: the size of the allocation */
- int index;
- /* see flags above for descriptions */
- unsigned int flags;
- /* protects this data field, if the mm_mmap sem will be held at the
- * same time as this sem, the mm sem must be taken first (as this is
- * the order for vma_open and vma_close ops */
- struct rw_semaphore sem;
- /* info about the mmaping process */
- struct vm_area_struct *vma;
- /* task struct of the mapping process */
- struct task_struct *task;
- /* process id of teh mapping process */
- pid_t pid;
- /* file descriptor of the master */
- int master_fd;
- /* file struct of the master */
- struct file *master_file;
- /* a list of currently available regions if this is a suballocation */
- struct list_head region_list;
- /* a linked list of data so we can access them for debugging */
- struct list_head list;
-#if PMEM_DEBUG
- int ref;
-#endif
-};
-
-struct pmem_bits {
- unsigned allocated:1; /* 1 if allocated, 0 if free */
- unsigned order:7; /* size of the region in pmem space */
-};
-
-struct pmem_region_node {
- struct pmem_region region;
- struct list_head list;
-};
-
-#define PMEM_DEBUG_MSGS 0
-#if PMEM_DEBUG_MSGS
-#define DLOG(fmt,args...) \
- do { printk(KERN_INFO "[%s:%s:%d] "fmt, __FILE__, __func__, __LINE__, \
- ##args); } \
- while (0)
-#else
-#define DLOG(x...) do {} while (0)
-#endif
-
-struct pmem_info {
- struct miscdevice dev;
- /* physical start address of the remaped pmem space */
- unsigned long base;
- /* vitual start address of the remaped pmem space */
- unsigned char __iomem *vbase;
- /* total size of the pmem space */
- unsigned long size;
- /* number of entries in the pmem space */
- unsigned long num_entries;
- /* pfn of the garbage page in memory */
- unsigned long garbage_pfn;
- /* index of the garbage page in the pmem space */
- int garbage_index;
- /* the bitmap for the region indicating which entries are allocated
- * and which are free */
- struct pmem_bits *bitmap;
- /* indicates the region should not be managed with an allocator */
- unsigned no_allocator;
- /* indicates maps of this region should be cached, if a mix of
- * cached and uncached is desired, set this and open the device with
- * O_SYNC to get an uncached region */
- unsigned cached;
- unsigned buffered;
- /* in no_allocator mode the first mapper gets the whole space and sets
- * this flag */
- unsigned allocated;
- /* for debugging, creates a list of pmem file structs, the
- * data_list_lock should be taken before pmem_data->sem if both are
- * needed */
- struct mutex data_list_lock;
- struct list_head data_list;
- /* pmem_sem protects the bitmap array
- * a write lock should be held when modifying entries in bitmap
- * a read lock should be held when reading data from bits or
- * dereferencing a pointer into bitmap
- *
- * pmem_data->sem protects the pmem data of a particular file
- * Many of the function that require the pmem_data->sem have a non-
- * locking version for when the caller is already holding that sem.
- *
- * IF YOU TAKE BOTH LOCKS TAKE THEM IN THIS ORDER:
- * down(pmem_data->sem) => down(bitmap_sem)
- */
- struct rw_semaphore bitmap_sem;
-
- long (*ioctl)(struct file *, unsigned int, unsigned long);
- int (*release)(struct inode *, struct file *);
-};
-
-static struct pmem_info pmem[PMEM_MAX_DEVICES];
-static int id_count;
-
-#define PMEM_IS_FREE(id, index) !(pmem[id].bitmap[index].allocated)
-#define PMEM_ORDER(id, index) pmem[id].bitmap[index].order
-#define PMEM_BUDDY_INDEX(id, index) (index ^ (1 << PMEM_ORDER(id, index)))
-#define PMEM_NEXT_INDEX(id, index) (index + (1 << PMEM_ORDER(id, index)))
-#define PMEM_OFFSET(index) (index * PMEM_MIN_ALLOC)
-#define PMEM_START_ADDR(id, index) (PMEM_OFFSET(index) + pmem[id].base)
-#define PMEM_LEN(id, index) ((1 << PMEM_ORDER(id, index)) * PMEM_MIN_ALLOC)
-#define PMEM_END_ADDR(id, index) (PMEM_START_ADDR(id, index) + \
- PMEM_LEN(id, index))
-#define PMEM_START_VADDR(id, index) (PMEM_OFFSET(id, index) + pmem[id].vbase)
-#define PMEM_END_VADDR(id, index) (PMEM_START_VADDR(id, index) + \
- PMEM_LEN(id, index))
-#define PMEM_REVOKED(data) (data->flags & PMEM_FLAGS_REVOKED)
-#define PMEM_IS_PAGE_ALIGNED(addr) (!((addr) & (~PAGE_MASK)))
-#define PMEM_IS_SUBMAP(data) ((data->flags & PMEM_FLAGS_SUBMAP) && \
- (!(data->flags & PMEM_FLAGS_UNSUBMAP)))
-
-static int pmem_release(struct inode *, struct file *);
-static int pmem_mmap(struct file *, struct vm_area_struct *);
-static int pmem_open(struct inode *, struct file *);
-static long pmem_ioctl(struct file *, unsigned int, unsigned long);
-
-struct file_operations pmem_fops = {
- .release = pmem_release,
- .mmap = pmem_mmap,
- .open = pmem_open,
- .unlocked_ioctl = pmem_ioctl,
-};
-
-static int get_id(struct file *file)
-{
- return MINOR(file->f_dentry->d_inode->i_rdev);
-}
-
-int is_pmem_file(struct file *file)
-{
- int id;
-
- if (unlikely(!file || !file->f_dentry || !file->f_dentry->d_inode))
- return 0;
- id = get_id(file);
- if (unlikely(id >= PMEM_MAX_DEVICES))
- return 0;
- if (unlikely(file->f_dentry->d_inode->i_rdev !=
- MKDEV(MISC_MAJOR, pmem[id].dev.minor)))
- return 0;
- return 1;
-}
-
-static int has_allocation(struct file *file)
-{
- struct pmem_data *data;
- /* check is_pmem_file first if not accessed via pmem_file_ops */
-
- if (unlikely(!file->private_data))
- return 0;
- data = (struct pmem_data *)file->private_data;
- if (unlikely(data->index < 0))
- return 0;
- return 1;
-}
-
-static int is_master_owner(struct file *file)
-{
- struct file *master_file;
- struct pmem_data *data;
- int put_needed, ret = 0;
-
- if (!is_pmem_file(file) || !has_allocation(file))
- return 0;
- data = (struct pmem_data *)file->private_data;
- if (PMEM_FLAGS_MASTERMAP & data->flags)
- return 1;
- master_file = fget_light(data->master_fd, &put_needed);
- if (master_file && data->master_file == master_file)
- ret = 1;
- fput_light(master_file, put_needed);
- return ret;
-}
-
-static int pmem_free(int id, int index)
-{
- /* caller should hold the write lock on pmem_sem! */
- int buddy, curr = index;
- DLOG("index %d\n", index);
-
- if (pmem[id].no_allocator) {
- pmem[id].allocated = 0;
- return 0;
- }
- /* clean up the bitmap, merging any buddies */
- pmem[id].bitmap[curr].allocated = 0;
- /* find a slots buddy Buddy# = Slot# ^ (1 << order)
- * if the buddy is also free merge them
- * repeat until the buddy is not free or end of the bitmap is reached
- */
- do {
- buddy = PMEM_BUDDY_INDEX(id, curr);
- if (PMEM_IS_FREE(id, buddy) &&
- PMEM_ORDER(id, buddy) == PMEM_ORDER(id, curr)) {
- PMEM_ORDER(id, buddy)++;
- PMEM_ORDER(id, curr)++;
- curr = min(buddy, curr);
- } else {
- break;
- }
- } while (curr < pmem[id].num_entries);
-
- return 0;
-}
-
-static void pmem_revoke(struct file *file, struct pmem_data *data);
-
-static int pmem_release(struct inode *inode, struct file *file)
-{
- struct pmem_data *data = (struct pmem_data *)file->private_data;
- struct pmem_region_node *region_node;
- struct list_head *elt, *elt2;
- int id = get_id(file), ret = 0;
-
-
- mutex_lock(&pmem[id].data_list_lock);
- /* if this file is a master, revoke all the memory in the connected
- * files */
- if (PMEM_FLAGS_MASTERMAP & data->flags) {
- struct pmem_data *sub_data;
- list_for_each(elt, &pmem[id].data_list) {
- sub_data = list_entry(elt, struct pmem_data, list);
- down_read(&sub_data->sem);
- if (PMEM_IS_SUBMAP(sub_data) &&
- file == sub_data->master_file) {
- up_read(&sub_data->sem);
- pmem_revoke(file, sub_data);
- } else
- up_read(&sub_data->sem);
- }
- }
- list_del(&data->list);
- mutex_unlock(&pmem[id].data_list_lock);
-
-
- down_write(&data->sem);
-
- /* if its not a conencted file and it has an allocation, free it */
- if (!(PMEM_FLAGS_CONNECTED & data->flags) && has_allocation(file)) {
- down_write(&pmem[id].bitmap_sem);
- ret = pmem_free(id, data->index);
- up_write(&pmem[id].bitmap_sem);
- }
-
- /* if this file is a submap (mapped, connected file), downref the
- * task struct */
- if (PMEM_FLAGS_SUBMAP & data->flags)
- if (data->task) {
- put_task_struct(data->task);
- data->task = NULL;
- }
-
- file->private_data = NULL;
-
- list_for_each_safe(elt, elt2, &data->region_list) {
- region_node = list_entry(elt, struct pmem_region_node, list);
- list_del(elt);
- kfree(region_node);
- }
- BUG_ON(!list_empty(&data->region_list));
-
- up_write(&data->sem);
- kfree(data);
- if (pmem[id].release)
- ret = pmem[id].release(inode, file);
-
- return ret;
-}
-
-static int pmem_open(struct inode *inode, struct file *file)
-{
- struct pmem_data *data;
- int id = get_id(file);
- int ret = 0;
-
- DLOG("current %u file %p(%d)\n", current->pid, file, file_count(file));
- /* setup file->private_data to indicate its unmapped */
- /* you can only open a pmem device one time */
- if (file->private_data != NULL && file_count(file) != 1)
- return -1;
- data = kmalloc(sizeof(struct pmem_data), GFP_KERNEL);
- if (!data) {
- printk("pmem: unable to allocate memory for pmem metadata.");
- return -1;
- }
- data->flags = 0;
- data->index = -1;
- data->task = NULL;
- data->vma = NULL;
- data->pid = 0;
- data->master_file = NULL;
-#if PMEM_DEBUG
- data->ref = 0;
-#endif
- INIT_LIST_HEAD(&data->region_list);
- init_rwsem(&data->sem);
-
- file->private_data = data;
- INIT_LIST_HEAD(&data->list);
-
- mutex_lock(&pmem[id].data_list_lock);
- list_add(&data->list, &pmem[id].data_list);
- mutex_unlock(&pmem[id].data_list_lock);
- return ret;
-}
-
-static unsigned long pmem_order(unsigned long len)
-{
- int i;
-
- len = (len + PMEM_MIN_ALLOC - 1)/PMEM_MIN_ALLOC;
- len--;
- for (i = 0; i < sizeof(len)*8; i++)
- if (len >> i == 0)
- break;
- return i;
-}
-
-static int pmem_allocate(int id, unsigned long len)
-{
- /* caller should hold the write lock on pmem_sem! */
- /* return the corresponding pdata[] entry */
- int curr = 0;
- int end = pmem[id].num_entries;
- int best_fit = -1;
- unsigned long order = pmem_order(len);
-
- if (pmem[id].no_allocator) {
- DLOG("no allocator");
- if ((len > pmem[id].size) || pmem[id].allocated)
- return -1;
- pmem[id].allocated = 1;
- return len;
- }
-
- if (order > PMEM_MAX_ORDER)
- return -1;
- DLOG("order %lx\n", order);
-
- /* look through the bitmap:
- * if you find a free slot of the correct order use it
- * otherwise, use the best fit (smallest with size > order) slot
- */
- while (curr < end) {
- if (PMEM_IS_FREE(id, curr)) {
- if (PMEM_ORDER(id, curr) == (unsigned char)order) {
- /* set the not free bit and clear others */
- best_fit = curr;
- break;
- }
- if (PMEM_ORDER(id, curr) > (unsigned char)order &&
- (best_fit < 0 ||
- PMEM_ORDER(id, curr) < PMEM_ORDER(id, best_fit)))
- best_fit = curr;
- }
- curr = PMEM_NEXT_INDEX(id, curr);
- }
-
- /* if best_fit < 0, there are no suitable slots,
- * return an error
- */
- if (best_fit < 0) {
- printk("pmem: no space left to allocate!\n");
- return -1;
- }
-
- /* now partition the best fit:
- * split the slot into 2 buddies of order - 1
- * repeat until the slot is of the correct order
- */
- while (PMEM_ORDER(id, best_fit) > (unsigned char)order) {
- int buddy;
- PMEM_ORDER(id, best_fit) -= 1;
- buddy = PMEM_BUDDY_INDEX(id, best_fit);
- PMEM_ORDER(id, buddy) = PMEM_ORDER(id, best_fit);
- }
- pmem[id].bitmap[best_fit].allocated = 1;
- return best_fit;
-}
-
-static pgprot_t pmem_access_prot(struct file *file, pgprot_t vma_prot)
-{
- int id = get_id(file);
-#ifdef pgprot_noncached
- if (pmem[id].cached == 0 || file->f_flags & O_SYNC)
- return pgprot_noncached(vma_prot);
-#endif
-#ifdef pgprot_ext_buffered
- else if (pmem[id].buffered)
- return pgprot_ext_buffered(vma_prot);
-#endif
- return vma_prot;
-}
-
-static unsigned long pmem_start_addr(int id, struct pmem_data *data)
-{
- if (pmem[id].no_allocator)
- return PMEM_START_ADDR(id, 0);
- else
- return PMEM_START_ADDR(id, data->index);
-
-}
-
-static void *pmem_start_vaddr(int id, struct pmem_data *data)
-{
- return pmem_start_addr(id, data) - pmem[id].base + pmem[id].vbase;
-}
-
-static unsigned long pmem_len(int id, struct pmem_data *data)
-{
- if (pmem[id].no_allocator)
- return data->index;
- else
- return PMEM_LEN(id, data->index);
-}
-
-static int pmem_map_garbage(int id, struct vm_area_struct *vma,
- struct pmem_data *data, unsigned long offset,
- unsigned long len)
-{
- int i, garbage_pages = len >> PAGE_SHIFT;
-
- vma->vm_flags |= VM_IO | VM_RESERVED | VM_PFNMAP | VM_SHARED | VM_WRITE;
- for (i = 0; i < garbage_pages; i++) {
- if (vm_insert_pfn(vma, vma->vm_start + offset + (i * PAGE_SIZE),
- pmem[id].garbage_pfn))
- return -EAGAIN;
- }
- return 0;
-}
-
-static int pmem_unmap_pfn_range(int id, struct vm_area_struct *vma,
- struct pmem_data *data, unsigned long offset,
- unsigned long len)
-{
- int garbage_pages;
- DLOG("unmap offset %lx len %lx\n", offset, len);
-
- BUG_ON(!PMEM_IS_PAGE_ALIGNED(len));
-
- garbage_pages = len >> PAGE_SHIFT;
- zap_page_range(vma, vma->vm_start + offset, len, NULL);
- pmem_map_garbage(id, vma, data, offset, len);
- return 0;
-}
-
-static int pmem_map_pfn_range(int id, struct vm_area_struct *vma,
- struct pmem_data *data, unsigned long offset,
- unsigned long len)
-{
- DLOG("map offset %lx len %lx\n", offset, len);
- BUG_ON(!PMEM_IS_PAGE_ALIGNED(vma->vm_start));
- BUG_ON(!PMEM_IS_PAGE_ALIGNED(vma->vm_end));
- BUG_ON(!PMEM_IS_PAGE_ALIGNED(len));
- BUG_ON(!PMEM_IS_PAGE_ALIGNED(offset));
-
- if (io_remap_pfn_range(vma, vma->vm_start + offset,
- (pmem_start_addr(id, data) + offset) >> PAGE_SHIFT,
- len, vma->vm_page_prot)) {
- return -EAGAIN;
- }
- return 0;
-}
-
-static int pmem_remap_pfn_range(int id, struct vm_area_struct *vma,
- struct pmem_data *data, unsigned long offset,
- unsigned long len)
-{
- /* hold the mm semp for the vma you are modifying when you call this */
- BUG_ON(!vma);
- zap_page_range(vma, vma->vm_start + offset, len, NULL);
- return pmem_map_pfn_range(id, vma, data, offset, len);
-}
-
-static void pmem_vma_open(struct vm_area_struct *vma)
-{
- struct file *file = vma->vm_file;
- struct pmem_data *data = file->private_data;
- int id = get_id(file);
- /* this should never be called as we don't support copying pmem
- * ranges via fork */
- BUG_ON(!has_allocation(file));
- down_write(&data->sem);
- /* remap the garbage pages, forkers don't get access to the data */
- pmem_unmap_pfn_range(id, vma, data, 0, vma->vm_start - vma->vm_end);
- up_write(&data->sem);
-}
-
-static void pmem_vma_close(struct vm_area_struct *vma)
-{
- struct file *file = vma->vm_file;
- struct pmem_data *data = file->private_data;
-
- DLOG("current %u ppid %u file %p count %d\n", current->pid,
- current->parent->pid, file, file_count(file));
- if (unlikely(!is_pmem_file(file) || !has_allocation(file))) {
- printk(KERN_WARNING "pmem: something is very wrong, you are "
- "closing a vm backing an allocation that doesn't "
- "exist!\n");
- return;
- }
- down_write(&data->sem);
- if (data->vma == vma) {
- data->vma = NULL;
- if ((data->flags & PMEM_FLAGS_CONNECTED) &&
- (data->flags & PMEM_FLAGS_SUBMAP))
- data->flags |= PMEM_FLAGS_UNSUBMAP;
- }
- /* the kernel is going to free this vma now anyway */
- up_write(&data->sem);
-}
-
-static struct vm_operations_struct vm_ops = {
- .open = pmem_vma_open,
- .close = pmem_vma_close,
-};
-
-static int pmem_mmap(struct file *file, struct vm_area_struct *vma)
-{
- struct pmem_data *data;
- int index;
- unsigned long vma_size = vma->vm_end - vma->vm_start;
- int ret = 0, id = get_id(file);
-
- if (vma->vm_pgoff || !PMEM_IS_PAGE_ALIGNED(vma_size)) {
-#if PMEM_DEBUG
- printk(KERN_ERR "pmem: mmaps must be at offset zero, aligned"
- " and a multiple of pages_size.\n");
-#endif
- return -EINVAL;
- }
-
- data = (struct pmem_data *)file->private_data;
- down_write(&data->sem);
- /* check this file isn't already mmaped, for submaps check this file
- * has never been mmaped */
- if ((data->flags & PMEM_FLAGS_SUBMAP) ||
- (data->flags & PMEM_FLAGS_UNSUBMAP)) {
-#if PMEM_DEBUG
- printk(KERN_ERR "pmem: you can only mmap a pmem file once, "
- "this file is already mmaped. %x\n", data->flags);
-#endif
- ret = -EINVAL;
- goto error;
- }
- /* if file->private_data == unalloced, alloc*/
- if (data && data->index == -1) {
- down_write(&pmem[id].bitmap_sem);
- index = pmem_allocate(id, vma->vm_end - vma->vm_start);
- up_write(&pmem[id].bitmap_sem);
- data->index = index;
- }
- /* either no space was available or an error occured */
- if (!has_allocation(file)) {
- ret = -EINVAL;
- printk("pmem: could not find allocation for map.\n");
- goto error;
- }
-
- if (pmem_len(id, data) < vma_size) {
-#if PMEM_DEBUG
- printk(KERN_WARNING "pmem: mmap size [%lu] does not match"
- "size of backing region [%lu].\n", vma_size,
- pmem_len(id, data));
-#endif
- ret = -EINVAL;
- goto error;
- }
-
- vma->vm_pgoff = pmem_start_addr(id, data) >> PAGE_SHIFT;
- vma->vm_page_prot = pmem_access_prot(file, vma->vm_page_prot);
-
- if (data->flags & PMEM_FLAGS_CONNECTED) {
- struct pmem_region_node *region_node;
- struct list_head *elt;
- if (pmem_map_garbage(id, vma, data, 0, vma_size)) {
- printk("pmem: mmap failed in kernel!\n");
- ret = -EAGAIN;
- goto error;
- }
- list_for_each(elt, &data->region_list) {
- region_node = list_entry(elt, struct pmem_region_node,
- list);
- DLOG("remapping file: %p %lx %lx\n", file,
- region_node->region.offset,
- region_node->region.len);
- if (pmem_remap_pfn_range(id, vma, data,
- region_node->region.offset,
- region_node->region.len)) {
- ret = -EAGAIN;
- goto error;
- }
- }
- data->flags |= PMEM_FLAGS_SUBMAP;
- get_task_struct(current->group_leader);
- data->task = current->group_leader;
- data->vma = vma;
-#if PMEM_DEBUG
- data->pid = current->pid;
-#endif
- DLOG("submmapped file %p vma %p pid %u\n", file, vma,
- current->pid);
- } else {
- if (pmem_map_pfn_range(id, vma, data, 0, vma_size)) {
- printk(KERN_INFO "pmem: mmap failed in kernel!\n");
- ret = -EAGAIN;
- goto error;
- }
- data->flags |= PMEM_FLAGS_MASTERMAP;
- data->pid = current->pid;
- }
- vma->vm_ops = &vm_ops;
-error:
- up_write(&data->sem);
- return ret;
-}
-
-/* the following are the api for accessing pmem regions by other drivers
- * from inside the kernel */
-int get_pmem_user_addr(struct file *file, unsigned long *start,
- unsigned long *len)
-{
- struct pmem_data *data;
- if (!is_pmem_file(file) || !has_allocation(file)) {
-#if PMEM_DEBUG
- printk(KERN_INFO "pmem: requested pmem data from invalid"
- "file.\n");
-#endif
- return -1;
- }
- data = (struct pmem_data *)file->private_data;
- down_read(&data->sem);
- if (data->vma) {
- *start = data->vma->vm_start;
- *len = data->vma->vm_end - data->vma->vm_start;
- } else {
- *start = 0;
- *len = 0;
- }
- up_read(&data->sem);
- return 0;
-}
-
-int get_pmem_addr(struct file *file, unsigned long *start,
- unsigned long *vstart, unsigned long *len)
-{
- struct pmem_data *data;
- int id;
-
- if (!is_pmem_file(file) || !has_allocation(file)) {
- return -1;
- }
-
- data = (struct pmem_data *)file->private_data;
- if (data->index == -1) {
-#if PMEM_DEBUG
- printk(KERN_INFO "pmem: requested pmem data from file with no "
- "allocation.\n");
- return -1;
-#endif
- }
- id = get_id(file);
-
- down_read(&data->sem);
- *start = pmem_start_addr(id, data);
- *len = pmem_len(id, data);
- *vstart = (unsigned long)pmem_start_vaddr(id, data);
- up_read(&data->sem);
-#if PMEM_DEBUG
- down_write(&data->sem);
- data->ref++;
- up_write(&data->sem);
-#endif
- return 0;
-}
-
-int get_pmem_file(int fd, unsigned long *start, unsigned long *vstart,
- unsigned long *len, struct file **filp)
-{
- struct file *file;
-
- file = fget(fd);
- if (unlikely(file == NULL)) {
- printk(KERN_INFO "pmem: requested data from file descriptor "
- "that doesn't exist.");
- return -1;
- }
-
- if (get_pmem_addr(file, start, vstart, len))
- goto end;
-
- if (filp)
- *filp = file;
- return 0;
-end:
- fput(file);
- return -1;
-}
-
-void put_pmem_file(struct file *file)
-{
- struct pmem_data *data;
- int id;
-
- if (!is_pmem_file(file))
- return;
- id = get_id(file);
- data = (struct pmem_data *)file->private_data;
-#if PMEM_DEBUG
- down_write(&data->sem);
- if (data->ref == 0) {
- printk("pmem: pmem_put > pmem_get %s (pid %d)\n",
- pmem[id].dev.name, data->pid);
- BUG();
- }
- data->ref--;
- up_write(&data->sem);
-#endif
- fput(file);
-}
-
-void flush_pmem_file(struct file *file, unsigned long offset, unsigned long len)
-{
- struct pmem_data *data;
- int id;
- void *vaddr;
- struct pmem_region_node *region_node;
- struct list_head *elt;
- void *flush_start, *flush_end;
-
- if (!is_pmem_file(file) || !has_allocation(file)) {
- return;
- }
-
- id = get_id(file);
- data = (struct pmem_data *)file->private_data;
- if (!pmem[id].cached || file->f_flags & O_SYNC)
- return;
-
- down_read(&data->sem);
- vaddr = pmem_start_vaddr(id, data);
- /* if this isn't a submmapped file, flush the whole thing */
- if (unlikely(!(data->flags & PMEM_FLAGS_CONNECTED))) {
- if (pmem_len(id, data) >= SZ_1M) {
- flush_all_cpu_caches();
- outer_flush_all();
- } else if (pmem_len(id, data) >= SZ_64K) {
- flush_all_cpu_caches();
- outer_flush_range(virt_to_phys(vaddr), virt_to_phys(vaddr + pmem_len(id, data)));
- } else {
- dmac_flush_range(vaddr, vaddr + pmem_len(id, data));
- outer_flush_range(virt_to_phys(vaddr), virt_to_phys(vaddr + pmem_len(id, data)));
- }
- goto end;
- }
- /* otherwise, flush the region of the file we are drawing */
- list_for_each(elt, &data->region_list) {
- region_node = list_entry(elt, struct pmem_region_node, list);
- if ((offset >= region_node->region.offset) &&
- ((offset + len) <= (region_node->region.offset +
- region_node->region.len))) {
- flush_start = vaddr + region_node->region.offset;
- flush_end = flush_start + region_node->region.len;
-
- if (pmem_len(id, data) >= SZ_1M) {
- flush_all_cpu_caches();
- outer_flush_all();
- } else if (pmem_len(id, data) >= SZ_64K) {
- flush_all_cpu_caches();
- outer_flush_range(virt_to_phys(flush_start), virt_to_phys(flush_end));
- } else {
- dmac_flush_range(flush_start, flush_end);
- outer_flush_range(virt_to_phys(flush_start), virt_to_phys(flush_end));
- }
- break;
- }
- }
-end:
- up_read(&data->sem);
-}
-
-static int pmem_connect(unsigned long connect, struct file *file)
-{
- struct pmem_data *data = (struct pmem_data *)file->private_data;
- struct pmem_data *src_data;
- struct file *src_file;
- int ret = 0, put_needed;
-
- down_write(&data->sem);
- /* retrieve the src file and check it is a pmem file with an alloc */
- src_file = fget_light(connect, &put_needed);
- DLOG("connect %p to %p\n", file, src_file);
- if (!src_file) {
- printk("pmem: src file not found!\n");
- ret = -EINVAL;
- goto err_no_file;
- }
- if (unlikely(!is_pmem_file(src_file) || !has_allocation(src_file))) {
- printk(KERN_INFO "pmem: src file is not a pmem file or has no "
- "alloc!\n");
- ret = -EINVAL;
- goto err_bad_file;
- }
- src_data = (struct pmem_data *)src_file->private_data;
-
- if (has_allocation(file) && (data->index != src_data->index)) {
- printk("pmem: file is already mapped but doesn't match this"
- " src_file!\n");
- ret = -EINVAL;
- goto err_bad_file;
- }
- data->index = src_data->index;
- data->flags |= PMEM_FLAGS_CONNECTED;
- data->master_fd = connect;
- data->master_file = src_file;
-
-err_bad_file:
- fput_light(src_file, put_needed);
-err_no_file:
- up_write(&data->sem);
- return ret;
-}
-
-static void pmem_unlock_data_and_mm(struct pmem_data *data,
- struct mm_struct *mm)
-{
- up_write(&data->sem);
- if (mm != NULL) {
- up_write(&mm->mmap_sem);
- mmput(mm);
- }
-}
-
-static int pmem_lock_data_and_mm(struct file *file, struct pmem_data *data,
- struct mm_struct **locked_mm)
-{
- int ret = 0;
- struct mm_struct *mm = NULL;
- *locked_mm = NULL;
-lock_mm:
- down_read(&data->sem);
- if (PMEM_IS_SUBMAP(data)) {
- mm = get_task_mm(data->task);
- if (!mm) {
-#if PMEM_DEBUG
- printk("pmem: can't remap task is gone!\n");
-#endif
- up_read(&data->sem);
- return -1;
- }
- }
- up_read(&data->sem);
-
- if (mm)
- down_write(&mm->mmap_sem);
-
- down_write(&data->sem);
- /* check that the file didn't get mmaped before we could take the
- * data sem, this should be safe b/c you can only submap each file
- * once */
- if (PMEM_IS_SUBMAP(data) && !mm) {
- pmem_unlock_data_and_mm(data, mm);
- goto lock_mm;
- }
- /* now check that vma.mm is still there, it could have been
- * deleted by vma_close before we could get the data->sem */
- if ((data->flags & PMEM_FLAGS_UNSUBMAP) && (mm != NULL)) {
- /* might as well release this */
- if (data->flags & PMEM_FLAGS_SUBMAP) {
- put_task_struct(data->task);
- data->task = NULL;
- /* lower the submap flag to show the mm is gone */
- data->flags &= ~(PMEM_FLAGS_SUBMAP);
- }
- pmem_unlock_data_and_mm(data, mm);
- return -1;
- }
- *locked_mm = mm;
- return ret;
-}
-
-int pmem_remap(struct pmem_region *region, struct file *file,
- unsigned operation)
-{
- int ret;
- struct pmem_region_node *region_node;
- struct mm_struct *mm = NULL;
- struct list_head *elt, *elt2;
- int id = get_id(file);
- struct pmem_data *data = (struct pmem_data *)file->private_data;
-
- /* pmem region must be aligned on a page boundry */
- if (unlikely(!PMEM_IS_PAGE_ALIGNED(region->offset) ||
- !PMEM_IS_PAGE_ALIGNED(region->len))) {
-#if PMEM_DEBUG
- printk("pmem: request for unaligned pmem suballocation "
- "%lx %lx\n", region->offset, region->len);
-#endif
- return -EINVAL;
- }
-
- /* if userspace requests a region of len 0, there's nothing to do */
- if (region->len == 0)
- return 0;
-
- /* lock the mm and data */
- ret = pmem_lock_data_and_mm(file, data, &mm);
- if (ret)
- return 0;
-
- /* only the owner of the master file can remap the client fds
- * that back in it */
- if (!is_master_owner(file)) {
-#if PMEM_DEBUG
- printk("pmem: remap requested from non-master process\n");
-#endif
- ret = -EINVAL;
- goto err;
- }
-
- /* check that the requested range is within the src allocation */
- if (unlikely((region->offset > pmem_len(id, data)) ||
- (region->len > pmem_len(id, data)) ||
- (region->offset + region->len > pmem_len(id, data)))) {
-#if PMEM_DEBUG
- printk(KERN_INFO "pmem: suballoc doesn't fit in src_file!\n");
-#endif
- ret = -EINVAL;
- goto err;
- }
-
- if (operation == PMEM_MAP) {
- region_node = kmalloc(sizeof(struct pmem_region_node),
- GFP_KERNEL);
- if (!region_node) {
- ret = -ENOMEM;
-#if PMEM_DEBUG
- printk(KERN_INFO "No space to allocate metadata!");
-#endif
- goto err;
- }
- region_node->region = *region;
- list_add(&region_node->list, &data->region_list);
- } else if (operation == PMEM_UNMAP) {
- int found = 0;
- list_for_each_safe(elt, elt2, &data->region_list) {
- region_node = list_entry(elt, struct pmem_region_node,
- list);
- if (region->len == 0 ||
- (region_node->region.offset == region->offset &&
- region_node->region.len == region->len)) {
- list_del(elt);
- kfree(region_node);
- found = 1;
- }
- }
- if (!found) {
-#if PMEM_DEBUG
- printk("pmem: Unmap region does not map any mapped "
- "region!");
-#endif
- ret = -EINVAL;
- goto err;
- }
- }
-
- if (data->vma && PMEM_IS_SUBMAP(data)) {
- if (operation == PMEM_MAP)
- ret = pmem_remap_pfn_range(id, data->vma, data,
- region->offset, region->len);
- else if (operation == PMEM_UNMAP)
- ret = pmem_unmap_pfn_range(id, data->vma, data,
- region->offset, region->len);
- }
-
-err:
- pmem_unlock_data_and_mm(data, mm);
- return ret;
-}
-
-static void pmem_revoke(struct file *file, struct pmem_data *data)
-{
- struct pmem_region_node *region_node;
- struct list_head *elt, *elt2;
- struct mm_struct *mm = NULL;
- int id = get_id(file);
- int ret = 0;
-
- data->master_file = NULL;
- ret = pmem_lock_data_and_mm(file, data, &mm);
- /* if lock_data_and_mm fails either the task that mapped the fd, or
- * the vma that mapped it have already gone away, nothing more
- * needs to be done */
- if (ret)
- return;
- /* unmap everything */
- /* delete the regions and region list nothing is mapped any more */
- if (data->vma)
- list_for_each_safe(elt, elt2, &data->region_list) {
- region_node = list_entry(elt, struct pmem_region_node,
- list);
- pmem_unmap_pfn_range(id, data->vma, data,
- region_node->region.offset,
- region_node->region.len);
- list_del(elt);
- kfree(region_node);
- }
- /* delete the master file */
- pmem_unlock_data_and_mm(data, mm);
-}
-
-static void pmem_get_size(struct pmem_region *region, struct file *file)
-{
- struct pmem_data *data = (struct pmem_data *)file->private_data;
- int id = get_id(file);
-
- if (!has_allocation(file)) {
- region->offset = 0;
- region->len = 0;
- return;
- } else {
- region->offset = pmem_start_addr(id, data);
- region->len = pmem_len(id, data);
- }
- DLOG("offset %lx len %lx\n", region->offset, region->len);
-}
-
-
-static long pmem_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
-{
- struct pmem_data *data;
- int id = get_id(file);
-
- switch (cmd) {
- case PMEM_GET_PHYS:
- {
- struct pmem_region region;
- DLOG("get_phys\n");
- if (!has_allocation(file)) {
- region.offset = 0;
- region.len = 0;
- } else {
- data = (struct pmem_data *)file->private_data;
- region.offset = pmem_start_addr(id, data);
- region.len = pmem_len(id, data);
- }
- printk(KERN_DEBUG "pmem: request for physical address of pmem region "
- "from process %d.\n", current->pid);
- if (copy_to_user((void __user *)arg, &region,
- sizeof(struct pmem_region)))
- return -EFAULT;
- break;
- }
- case PMEM_MAP:
- {
- struct pmem_region region;
- if (copy_from_user(&region, (void __user *)arg,
- sizeof(struct pmem_region)))
- return -EFAULT;
- data = (struct pmem_data *)file->private_data;
- return pmem_remap(&region, file, PMEM_MAP);
- }
- break;
- case PMEM_UNMAP:
- {
- struct pmem_region region;
- if (copy_from_user(&region, (void __user *)arg,
- sizeof(struct pmem_region)))
- return -EFAULT;
- data = (struct pmem_data *)file->private_data;
- return pmem_remap(&region, file, PMEM_UNMAP);
- break;
- }
- case PMEM_GET_SIZE:
- {
- struct pmem_region region;
- DLOG("get_size\n");
- pmem_get_size(&region, file);
- if (copy_to_user((void __user *)arg, &region,
- sizeof(struct pmem_region)))
- return -EFAULT;
- break;
- }
- case PMEM_GET_TOTAL_SIZE:
- {
- struct pmem_region region;
- DLOG("get total size\n");
- region.offset = 0;
- get_id(file);
- region.len = pmem[id].size;
- if (copy_to_user((void __user *)arg, &region,
- sizeof(struct pmem_region)))
- return -EFAULT;
- break;
- }
- case PMEM_ALLOCATE:
- {
- if (has_allocation(file))
- return -EINVAL;
- data = (struct pmem_data *)file->private_data;
- data->index = pmem_allocate(id, arg);
- break;
- }
- case PMEM_CONNECT:
- DLOG("connect\n");
- return pmem_connect(arg, file);
- break;
- case PMEM_CACHE_FLUSH:
- {
- struct pmem_region region;
- DLOG("flush\n");
- if (copy_from_user(&region, (void __user *)arg,
- sizeof(struct pmem_region)))
- return -EFAULT;
- flush_pmem_file(file, region.offset, region.len);
- break;
- }
- default:
- if (pmem[id].ioctl)
- return pmem[id].ioctl(file, cmd, arg);
- return -EINVAL;
- }
- return 0;
-}
-
-#if PMEM_DEBUG
-static ssize_t debug_open(struct inode *inode, struct file *file)
-{
- file->private_data = inode->i_private;
- return 0;
-}
-
-static ssize_t debug_read(struct file *file, char __user *buf, size_t count,
- loff_t *ppos)
-{
- struct list_head *elt, *elt2;
- struct pmem_data *data;
- struct pmem_region_node *region_node;
- int id = (int)file->private_data;
- const int debug_bufmax = 4096;
- static char buffer[4096];
- int n = 0;
-
- DLOG("debug open\n");
- n = scnprintf(buffer, debug_bufmax,
- "pid #: mapped regions (offset, len) (offset,len)...\n");
-
- mutex_lock(&pmem[id].data_list_lock);
- list_for_each(elt, &pmem[id].data_list) {
- data = list_entry(elt, struct pmem_data, list);
- down_read(&data->sem);
- n += scnprintf(buffer + n, debug_bufmax - n, "pid %u:",
- data->pid);
- list_for_each(elt2, &data->region_list) {
- region_node = list_entry(elt2, struct pmem_region_node,
- list);
- n += scnprintf(buffer + n, debug_bufmax - n,
- "(%lx,%lx) ",
- region_node->region.offset,
- region_node->region.len);
- }
- n += scnprintf(buffer + n, debug_bufmax - n, "\n");
- up_read(&data->sem);
- }
- mutex_unlock(&pmem[id].data_list_lock);
-
- n++;
- buffer[n] = 0;
- return simple_read_from_buffer(buf, count, ppos, buffer, n);
-}
-
-static struct file_operations debug_fops = {
- .read = debug_read,
- .open = debug_open,
-};
-#endif
-
-#if 0
-static struct miscdevice pmem_dev = {
- .name = "pmem",
- .fops = &pmem_fops,
-};
-#endif
-
-int pmem_setup(struct android_pmem_platform_data *pdata,
- long (*ioctl)(struct file *, unsigned int, unsigned long),
- int (*release)(struct inode *, struct file *))
-{
- int err = 0;
- int i, index = 0;
- int id = id_count;
- struct page **pages;
- int nr_pages;
- int prot;
- id_count++;
-
-#if defined(CONFIG_S5P_MEM_CMA)
- pdata->start = cma_alloc_from(pdata->name, pdata->size, 0);
-#endif
-
- pmem[id].no_allocator = pdata->no_allocator;
- pmem[id].cached = pdata->cached;
- pmem[id].buffered = pdata->buffered;
- pmem[id].base = pdata->start;
- pmem[id].size = pdata->size;
- pmem[id].ioctl = ioctl;
- pmem[id].release = release;
- init_rwsem(&pmem[id].bitmap_sem);
- mutex_init(&pmem[id].data_list_lock);
- INIT_LIST_HEAD(&pmem[id].data_list);
- pmem[id].dev.name = pdata->name;
- pmem[id].dev.minor = id;
- pmem[id].dev.fops = &pmem_fops;
- printk(KERN_INFO "%s: %d init\n", pdata->name, pdata->cached);
-
- err = misc_register(&pmem[id].dev);
- if (err) {
- printk(KERN_ALERT "Unable to register pmem driver!\n");
- goto err_cant_register_device;
- }
- pmem[id].num_entries = pmem[id].size / PMEM_MIN_ALLOC;
-
- pmem[id].bitmap = kmalloc(pmem[id].num_entries *
- sizeof(struct pmem_bits), GFP_KERNEL);
- if (!pmem[id].bitmap)
- goto err_no_mem_for_metadata;
-
- memset(pmem[id].bitmap, 0, sizeof(struct pmem_bits) *
- pmem[id].num_entries);
-
- for (i = sizeof(pmem[id].num_entries) * 8 - 1; i >= 0; i--) {
- if ((pmem[id].num_entries) & 1<<i) {
- PMEM_ORDER(id, index) = i;
- index = PMEM_NEXT_INDEX(id, index);
- }
- }
-
- nr_pages = pmem[id].size >> PAGE_SHIFT;
- pages = kmalloc(nr_pages * sizeof(*pages), GFP_KERNEL);
- if (!pages)
- goto err_no_mem_for_pages;
-
- for (i = 0; i < nr_pages; i++)
- pages[i] = phys_to_page(pmem[id].base + i * PAGE_SIZE);
-
- if (pmem[id].cached)
- prot = PAGE_KERNEL;
- else if (pmem[id].buffered)
- prot = pgprot_writecombine(PAGE_KERNEL);
- else
- prot = pgprot_noncached(PAGE_KERNEL);
-
- pmem[id].vbase = vmap(pages, nr_pages, VM_MAP, prot);
-
- kfree(pages);
-
- if (pmem[id].vbase == 0)
- goto error_cant_remap;
-
- pmem[id].garbage_pfn = page_to_pfn(alloc_page(GFP_KERNEL));
- if (pmem[id].no_allocator)
- pmem[id].allocated = 0;
-
-#if PMEM_DEBUG
- debugfs_create_file(pdata->name, S_IFREG | S_IRUGO, NULL, (void *)id,
- &debug_fops);
-#endif
- return 0;
-error_cant_remap:
-err_no_mem_for_pages:
- kfree(pmem[id].bitmap);
-err_no_mem_for_metadata:
- misc_deregister(&pmem[id].dev);
-err_cant_register_device:
- return -1;
-}
-
-static int pmem_probe(struct platform_device *pdev)
-{
- struct android_pmem_platform_data *pdata;
-
- if (!pdev || !pdev->dev.platform_data) {
- printk(KERN_ALERT "Unable to probe pmem!\n");
- return -1;
- }
- pdata = pdev->dev.platform_data;
- return pmem_setup(pdata, NULL, NULL);
-}
-
-
-static int pmem_remove(struct platform_device *pdev)
-{
- int id = pdev->id;
- __free_page(pfn_to_page(pmem[id].garbage_pfn));
-#if defined(CONFIG_S5P_MEM_CMA)
- cma_free(((struct android_pmem_platform_data *)(pdev->dev.platform_data))->start);
-#endif
- misc_deregister(&pmem[id].dev);
- return 0;
-}
-
-static struct platform_driver pmem_driver = {
- .probe = pmem_probe,
- .remove = pmem_remove,
- .driver = { .name = "android_pmem" }
-};
-
-
-static int __init pmem_init(void)
-{
- return platform_driver_register(&pmem_driver);
-}
-
-static void __exit pmem_exit(void)
-{
- platform_driver_unregister(&pmem_driver);
-}
-
-module_init(pmem_init);
-module_exit(pmem_exit);
-
diff --git a/drivers/misc/tzic.c b/drivers/misc/tzic.c
index 6a5e3ba..643e078 100644
--- a/drivers/misc/tzic.c
+++ b/drivers/misc/tzic.c
@@ -23,7 +23,6 @@
#include <linux/sched.h>
#include <linux/list.h>
#include <linux/mutex.h>
-#include <linux/android_pmem.h>
#include <linux/io.h>
#include <linux/types.h>
#include <asm/smc.h>