aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/misc
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/misc')
-rw-r--r--drivers/misc/Kconfig76
-rw-r--r--drivers/misc/Makefile16
-rw-r--r--drivers/misc/cw_tty.c13
-rw-r--r--drivers/misc/es305.c8
-rw-r--r--drivers/misc/fm34_we395.c1499
-rw-r--r--drivers/misc/max77693-muic.c946
-rw-r--r--drivers/misc/max8893.c160
-rw-r--r--drivers/misc/max8997-muic.c1710
-rw-r--r--drivers/misc/modem_if/Kconfig29
-rw-r--r--drivers/misc/modem_if/Makefile10
-rw-r--r--drivers/misc/modem_if/modem_debug.c429
-rw-r--r--drivers/misc/modem_if/modem_link_device_dpram.c2169
-rw-r--r--drivers/misc/modem_if/modem_link_device_dpram.h390
-rw-r--r--drivers/misc/modem_if/modem_link_device_dpram_ext_op.c1175
-rw-r--r--[-rwxr-xr-x]drivers/misc/modem_if/modem_link_device_hsic.c122
-rw-r--r--[-rwxr-xr-x]drivers/misc/modem_if/modem_link_device_hsic.h7
-rw-r--r--drivers/misc/modem_if/modem_link_device_pld.c1981
-rw-r--r--drivers/misc/modem_if/modem_link_device_pld.h532
-rw-r--r--drivers/misc/modem_if/modem_link_device_pld_ext_op.c556
-rw-r--r--drivers/misc/modem_if/modem_link_device_spi.c1795
-rw-r--r--drivers/misc/modem_if/modem_link_device_spi.h257
-rw-r--r--drivers/misc/modem_if/modem_link_device_usb.c95
-rw-r--r--drivers/misc/modem_if/modem_link_device_usb.h2
-rw-r--r--drivers/misc/modem_if/modem_link_pm_usb.c183
-rw-r--r--drivers/misc/modem_if/modem_link_pm_usb.h15
-rw-r--r--drivers/misc/modem_if/modem_modemctl_device_cbp72.c15
-rw-r--r--drivers/misc/modem_if/modem_modemctl_device_cmc221.c84
-rw-r--r--drivers/misc/modem_if/modem_modemctl_device_esc6270.c339
-rw-r--r--drivers/misc/modem_if/modem_modemctl_device_mdm6600.c307
-rw-r--r--drivers/misc/modem_if/modem_modemctl_device_sprd8803.c230
-rw-r--r--drivers/misc/modem_if/modem_modemctl_device_xmm6262.c37
-rw-r--r--drivers/misc/modem_if/modem_prj.h289
-rw-r--r--drivers/misc/modem_if/modem_sim_slot_switch.c92
-rw-r--r--drivers/misc/modem_if/modem_utils.c600
-rw-r--r--drivers/misc/modem_if/modem_utils.h142
-rw-r--r--drivers/misc/modem_if/modem_variation.h21
-rw-r--r--drivers/misc/modem_if/sipc4_io_device.c252
-rw-r--r--drivers/misc/modem_if/sipc4_modem.c103
-rw-r--r--drivers/misc/modem_if/sipc5_io_device.c878
-rw-r--r--drivers/misc/modem_if/sipc5_modem.c103
-rw-r--r--drivers/misc/modem_if_na/Kconfig30
-rw-r--r--drivers/misc/modem_if_na/Makefile8
-rw-r--r--drivers/misc/modem_if_na/lte_modem_bootloader.c320
-rw-r--r--drivers/misc/modem_if_na/modem.c221
-rw-r--r--drivers/misc/modem_if_na/modem_io_device.c965
-rw-r--r--drivers/misc/modem_if_na/modem_link_device_dpram.c1504
-rw-r--r--drivers/misc/modem_if_na/modem_link_device_dpram.h165
-rw-r--r--drivers/misc/modem_if_na/modem_link_device_usb.c1031
-rw-r--r--drivers/misc/modem_if_na/modem_link_device_usb.h148
-rw-r--r--drivers/misc/modem_if_na/modem_link_pm_usb.c370
-rw-r--r--drivers/misc/modem_if_na/modem_link_pm_usb.h88
-rw-r--r--drivers/misc/modem_if_na/modem_modemctl_device_cbp71.c269
-rw-r--r--drivers/misc/modem_if_na/modem_modemctl_device_cmc220.c254
-rw-r--r--drivers/misc/modem_if_na/modem_net_flowcontrol_device.c115
-rw-r--r--drivers/misc/modem_if_na/modem_prj.h239
-rw-r--r--drivers/misc/modem_if_na/modem_utils.c391
-rw-r--r--drivers/misc/modem_if_na/modem_utils.h158
-rw-r--r--drivers/misc/modem_if_na/modem_variation.h124
-rw-r--r--[-rwxr-xr-x]drivers/misc/mpu3050/accel/kxtf9.c190
-rw-r--r--drivers/misc/sec_jack.c64
-rw-r--r--drivers/misc/sec_jack_muic.c461
-rw-r--r--drivers/misc/tzic.c109
-rw-r--r--drivers/misc/usb3503_otg_conn.c378
-rw-r--r--drivers/misc/usb3803.c390
64 files changed, 21445 insertions, 4214 deletions
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index db4121a..24546ae 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -63,7 +63,7 @@ config AD525X_DPOT_SPI
config ANDROID_PMEM
bool "Android pmem allocator"
- depends on MACH_U1 || MACH_PX
+ depends on MACH_U1 || MACH_PX || MACH_TRATS
default n
if ANDROID_PMEM
@@ -558,6 +558,25 @@ config UART_SELECT
help
This is uart selection driver.
+config SWITCH_DUAL_MODEM
+ bool "Switch UART/USB to dual MODEM"
+ default n
+ help
+ This feature can be active when do dual CP project such as Grande.
+ This can offer an interface to switch UART/USB to AP/CP1/CP2 at *#7284# keystring application.
+ Need to change UI at *#7284# also to have effect on this.
+ Use a combination of MAX77693 switch function and GPIO control.
+
+config WIMAX_CMC
+ bool "SUB PMIC FOR WIMAX CMC"
+ depends on I2C
+ default n
+ help
+ This is feature for WIMAX ICS on gaudi Devices.
+ This is a 4G technology and connected through the SDIO interface 3.
+ This is powered through the max8893.
+ This feature is enabled by default if Wimax is enabled.
+
config SEC_DEV_JACK
bool "Earjack detection driver for Samsung devices"
depends on INPUT
@@ -565,6 +584,15 @@ config SEC_DEV_JACK
---help---
This is Earjack detection driver for Samsung devices.
+config MUIC_DET_JACK
+ bool "Earjack detection driver using MUIC"
+ default n
+ help
+ This is Earjack detection driver using MUIC.
+ MUIC can recognize earjack device type and earkey using ADC values.
+ Some project use muic earjack instead of 3.5phi earjack.
+ Max77693 muic also support earjack detection.
+
config FM34_WE395
bool "Forte Media 2MIC driver"
default n
@@ -603,12 +631,57 @@ config MUIC_MAX8997_OVPUI
is used in Chinese models. When OVP is occurred, the battery UI
must be changed to uncharging status.
+config MUIC_MAX77693_SEPARATE_MHL_PORT
+ bool "MUIC_MAX77693_SEPARATE_MHL_PORT"
+ depends on MFD_MAX77693
+ default n
+ help
+ If you say yes here you will get support for the separated
+ MHL port.
+
+config MUIC_MAX77693_SUPPORT_OTG_AUDIO_DOCK
+ bool "MUIC_MAX77693_SUPPORT_OTG_AUDIO_DOCK"
+ depends on MFD_MAX77693
+ default n
+ help
+ If you say yes here you will get support for the OTG audio dock.
+
+config MUIC_MAX77693_SUPPORT_SMART_DOCK
+ bool "MUIC_MAX77693_SUPPORT_SMART_DOCK"
+ depends on MFD_MAX77693
+ default n
+ help
+ If you say yes here you will get support for the smart dock.
+
+config MUIC_MAX77693_SUPPORT_CAR_DOCK
+ bool "MUIC_MAX77693_SUPPORT_CAR_DOCK"
+ depends on MFD_MAX77693
+ default n
+ help
+ If you say yes here you will get support for the car dock.
+
config USBHUB_USB3503
bool "USB3503 USB HUB Driver"
depends on I2C
help
Enables USB HUB USB3503
+config USBHUB_USB3503_OTG_CONN
+ bool "USB3503 USB HUB Driver"
+ depends on I2C
+ help
+ Enables USB HUB USB3503
+
+config USBHUB_USB3803
+ bool "USB3803 USB HUB Driver"
+ depends on I2C
+ help
+ Enables USB HUB USB3803. Say yes to use USB3803 usb hub
+ for the device. This hub is used to switch the usb
+ connection between AP and CP.
+
+ If unsure, say N
+
config PN544
bool "NXP PN544 NFC Controller Driver"
default n
@@ -644,6 +717,7 @@ source "drivers/misc/lis3lv02d/Kconfig"
source "drivers/misc/carma/Kconfig"
source "drivers/misc/c2c/Kconfig"
source "drivers/misc/modem_if/Kconfig"
+source "drivers/misc/modem_if_na/Kconfig"
config SLP_LOWMEM_NOTIFY
bool "Driver for SLP Low Memory Notification"
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 027da64..2c5dd1b 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -1,4 +1,4 @@
-#
+
# Makefile for misc devices that really don't fit anywhere else.
#
@@ -55,18 +55,32 @@ obj-$(CONFIG_APANIC) += apanic.o
obj-$(CONFIG_SENSORS_AK8975) += akm8975.o
obj-$(CONFIG_SAMSUNG_C2C) += c2c/
obj-$(CONFIG_USBHUB_USB3503) += usb3503.o
+obj-$(CONFIG_USBHUB_USB3503_OTG_CONN) += usb3503_otg_conn.o
+obj-$(CONFIG_USBHUB_USB3803) += usb3803.o
+ifeq ($(CONFIG_SEC_MODEM_P8LTE),y)
+obj-$(CONFIG_SEC_MODEM) += modem_if_na/
+else
obj-$(CONFIG_SEC_MODEM) += modem_if/
+endif
obj-$(CONFIG_MFD_MAX77693) += max77693-muic.o
obj-$(CONFIG_STMPE811_ADC) += stmpe811-adc.o
obj-$(CONFIG_JACK_MON) += jack.o
obj-$(CONFIG_UART_SELECT) += uart_select.o
+obj-$(CONFIG_WIMAX_CMC) += max8893.o
+ifeq ($(CONFIG_MUIC_DET_JACK),y)
+obj-$(CONFIG_SEC_DEV_JACK) += sec_jack_muic.o
+else
obj-$(CONFIG_SEC_DEV_JACK) += sec_jack.o
+endif
obj-$(CONFIG_MUIC_MAX8997) += max8997-muic.o
obj-$(CONFIG_PN544) += pn544.o
obj-$(CONFIG_FM34_WE395) += fm34_we395.o
obj-$(CONFIG_AUDIENCE_ES305) += es305.o
obj-y += 2mic/
+obj-$(CONFIG_SLP_PROCESS_MON) += slp_process_monitor.o
+obj-$(CONFIG_SLP_LOWMEM_NOTIFY) += slp_lowmem_notify.o
+
obj-$(CONFIG_MACH_M0_CTC) += cw_tty.o
# Secure OS Mobicore Interface
diff --git a/drivers/misc/cw_tty.c b/drivers/misc/cw_tty.c
index 4c6367b..016f8e2 100644
--- a/drivers/misc/cw_tty.c
+++ b/drivers/misc/cw_tty.c
@@ -127,9 +127,6 @@ static int XanCwReceivedWiFiData(int nDestChannelID,
}
if (dev->vs_dev.tty != NULL && dev->vs_dev.refcount) {
- pr_info("[%s] name: %s, len: %d\n", __func__,
- dev->vs_dev.tty_name, nRecvdBuflen);
-
ret = tty_insert_flip_string(dev->vs_dev.tty,
(u8 *)pi8_RecvdBuf, nRecvdBuflen);
if (ret < nRecvdBuflen)
@@ -164,9 +161,6 @@ static int XanCwSendToXanadu(int nSrcChannelID,
}
if (dev->vs_dev.tty != NULL && dev->vs_dev.refcount) {
- pr_info("[%s] name: %s, len: %d\n", __func__,
- dev->vs_dev.tty_name, nRecvdBuflen);
-
ret = tty_insert_flip_string(dev->vs_dev.tty,
(u8 *)pi8_RecvdBuf, nRecvdBuflen);
if (ret < nRecvdBuflen)
@@ -232,9 +226,6 @@ static int cw_tty_write(struct tty_struct *tty,
/* Added by Venkatesh GR, SISO For CDMA+WiFi requirements -- Start*/
if (strcmp(dev->vs_dev.tty_name, "ttyCWPPP") == 0) {
- pr_info("[%s] Rx PPPD, Tx CWTunnel : len = %d\n",
- __func__, count);
-
nDestChannelID = ID_CW_TTY_CWXAN;
/* Send the Buffer Received From PPPD to CWTunnel */
@@ -242,10 +233,6 @@ static int cw_tty_write(struct tty_struct *tty,
(u8 *)buf, count);
} else if (strcmp(dev->vs_dev.tty_name, "ttyCWXAN") == 0) {
- pr_info("[%s] Rx CWTunnel, Tx PPPD : len = %d\n",
- __func__, count);
-
-
nDestChannelID = ID_CW_TTY_CWPPP;
/* Send the Buffer Received From CWTunnel to PPPD */
diff --git a/drivers/misc/es305.c b/drivers/misc/es305.c
index f3f3cd0..0c1b469 100644
--- a/drivers/misc/es305.c
+++ b/drivers/misc/es305.c
@@ -28,11 +28,7 @@
#include <linux/firmware.h>
#include <linux/i2c/es305.h>
-#ifdef CONFIG_MACH_C1VZW
-#define ES305_FIRMWARE_NAME "audience/es305_fw_c1vzw.bin"
-#else
#define ES305_FIRMWARE_NAME "audience/es305_fw.bin"
-#endif
static struct i2c_client *this_client;
static struct es305_platform_data *pdata;
@@ -55,11 +51,7 @@ unsigned char es305_sleep_cmd[] = {
};
unsigned char es305_bypass_data[] = {
-#ifdef CONFIG_MACH_C1VZW
- 0x80, 0x52, 0x00, 0x48,
-#else
0x80, 0x52, 0x00, 0x4C,
-#endif
0x80, 0x10, 0x00, 0x01,
};
diff --git a/drivers/misc/fm34_we395.c b/drivers/misc/fm34_we395.c
index aa12226..ab2d57a 100644
--- a/drivers/misc/fm34_we395.c
+++ b/drivers/misc/fm34_we395.c
@@ -33,793 +33,804 @@
static struct i2c_client *this_client;
static struct fm34_platform_data *pdata;
-#if defined(CONFIG_MACH_C1_KOR_LGT) || defined(CONFIG_MACH_C1VZW)
+#if defined(CONFIG_MACH_C1_KOR_LGT)
unsigned char loopback_cmd[] = {
/*0xC0,*/
-0xFC, 0xF3, 0x3B, 0x22, 0xF5, 0x00, 0x04,
-0xFC, 0xF3, 0x3B, 0x22, 0xC6, 0x00, 0x7D,
-0xFC, 0xF3, 0x3B, 0x22, 0xC7, 0x00, 0x00,
-0xFC, 0xF3, 0x3B, 0x22, 0xC8, 0x00, 0x18,
-0xFC, 0xF3, 0x3B, 0x23, 0x0A, 0x1A, 0x00,
-0xFC, 0xF3, 0x3B, 0x22, 0xFA, 0x24, 0x8B,
-0xFC, 0xF3, 0x3B, 0x22, 0xF6, 0x00, 0x00,
-0xFC, 0xF3, 0x3B, 0x22, 0xD2, 0x82, 0x94,
-0xFC, 0xF3, 0x3B, 0x22, 0xEE, 0x00, 0x01,
-0xFC, 0xF3, 0x3B, 0x22, 0xF8, 0x80, 0x01,
-0xFC, 0xF3, 0x3B, 0x22, 0xF9, 0x08, 0x7F,
-0xFC, 0xF3, 0x3B, 0x23, 0x65, 0x08, 0x00,
-0xFC, 0xF3, 0x3B, 0x22, 0xC0, 0x00, 0x03,
-0xFC, 0xF3, 0x3B, 0x22, 0xC1, 0x00, 0x03,
-0xFC, 0xF3, 0x3B, 0x22, 0xC2, 0x00, 0x04,
-0xFC, 0xF3, 0x3B, 0x22, 0xC3, 0x00, 0x04,
-0xFC, 0xF3, 0x3B, 0x23, 0x07, 0x00, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x0C, 0x00, 0xB8,
-0xFC, 0xF3, 0x3B, 0x23, 0x0D, 0x02, 0x00,
-0xFC, 0xF3, 0x3B, 0x22, 0xFB, 0x00, 0x00
+ 0xFC, 0xF3, 0x3B, 0x22, 0xF5, 0x00, 0x04,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xC6, 0x00, 0x7D,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xC7, 0x00, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xC8, 0x00, 0x18,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x0A, 0x1A, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xFA, 0x24, 0x8B,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xF6, 0x00, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xD2, 0x82, 0x94,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xEE, 0x00, 0x01,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xF8, 0x80, 0x01,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xF9, 0x08, 0x7F,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x65, 0x08, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xC0, 0x00, 0x03,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xC1, 0x00, 0x03,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xC2, 0x00, 0x04,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xC3, 0x00, 0x04,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x07, 0x00, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x0C, 0x00, 0xB8,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x0D, 0x02, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xFB, 0x00, 0x00
};
unsigned char bypass_cmd[] = {
/*0xC0,*/
-0xFC, 0xF3, 0x3B, 0x22, 0xC0, 0x00, 0x00,
-0xFC, 0xF3, 0x3B, 0x22, 0xC1, 0x00, 0x01,
-0xFC, 0xF3, 0x3B, 0x22, 0xC2, 0x00, 0x02,
-0xFC, 0xF3, 0x3B, 0x22, 0xC3, 0x00, 0x02,
-0xFC, 0xF3, 0x3B, 0x22, 0xC6, 0x00, 0x7D,
-0xFC, 0xF3, 0x3B, 0x22, 0xC7, 0x00, 0x00,
-0xFC, 0xF3, 0x3B, 0x22, 0xC8, 0x00, 0x18,
-0xFC, 0xF3, 0x3B, 0x22, 0xD2, 0x82, 0x94,
-0xFC, 0xF3, 0x3B, 0x22, 0xEE, 0x00, 0x01,
-0xFC, 0xF3, 0x3B, 0x22, 0xF5, 0x00, 0x03,
-0xFC, 0xF3, 0x3B, 0x22, 0xF6, 0x00, 0x00,
-0xFC, 0xF3, 0x3B, 0x22, 0xF8, 0x80, 0x01,
-0xFC, 0xF3, 0x3B, 0x22, 0xF9, 0x08, 0x7F,
-0xFC, 0xF3, 0x3B, 0x22, 0xFA, 0x24, 0x8B,
-0xFC, 0xF3, 0x3B, 0x23, 0x07, 0x00, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x0A, 0x1A, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x0C, 0x00, 0xB8,
-0xFC, 0xF3, 0x3B, 0x23, 0x0D, 0x02, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x65, 0x08, 0x00,
-0xFC, 0xF3, 0x3B, 0x22, 0xFB, 0x00, 0x00
+ 0xFC, 0xF3, 0x3B, 0x22, 0xC0, 0x00, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xC1, 0x00, 0x01,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xC2, 0x00, 0x02,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xC3, 0x00, 0x02,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xC6, 0x00, 0x7D,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xC7, 0x00, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xC8, 0x00, 0x18,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xD2, 0x82, 0x94,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xEE, 0x00, 0x01,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xF5, 0x00, 0x03,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xF6, 0x00, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xF8, 0x80, 0x01,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xF9, 0x08, 0x7F,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xFA, 0x24, 0x8B,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x07, 0x00, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x0A, 0x1A, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x0C, 0x00, 0xB8,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x0D, 0x02, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x65, 0x08, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xFB, 0x00, 0x00
};
unsigned char HS_cmd[] = {
/*0xC0,*/
-0xFC, 0xF3, 0x68, 0x64, 0x04,
-0xFC, 0xF3, 0x3B, 0x3F, 0xE8, 0x00, 0x50,
-0xFC, 0xF3, 0x0D, 0x10, 0x00, 0x0C, 0x2C, 0x00,
-0xFC, 0xF3, 0x88,
-0x2E, 0xEC, 0xAC, 0x19, 0x00, 0x52, 0x47, 0xFF,
-0xF4, 0x19, 0x00, 0x6F, 0x1C, 0x99, 0x8F, 0x37,
-0x83, 0x01, 0x47, 0xC2, 0x97, 0x60, 0x00, 0xA5,
-0x62, 0xE2, 0xE6, 0x19, 0x00, 0xC4, 0x42, 0x66,
-0x67, 0x20, 0xCE, 0x0F, 0x0D, 0x00, 0xBC, 0x0D,
-0x00, 0xCD, 0x21, 0x0A, 0x0F, 0x6A, 0x64, 0xB4,
-0x80, 0x7A, 0x17, 0x68, 0x8A, 0xA6, 0x0E, 0x64,
-0x0F, 0x0E, 0x44, 0x0F, 0x10, 0x5B, 0x59, 0x90,
-0x7A, 0x1F, 0x80, 0x7A, 0x2A, 0x22, 0x6A, 0x0F,
-0x90, 0x7A, 0x2A, 0x34, 0x00, 0x0E, 0x1A, 0x61,
-0x0F,
-0xFC, 0xF3, 0x68, 0x64, 0x00,
-0xFC, 0xF3, 0x3B, 0x3F, 0xA1, 0xA6, 0x06,
-0xFC, 0xF3, 0x3B, 0x3F, 0xB1, 0x50, 0x00,
-0xFC, 0xF3, 0x3B, 0x22, 0xC6, 0x00, 0x7D,
-0xFC, 0xF3, 0x3B, 0x22, 0xC7, 0x00, 0x00,
-0xFC, 0xF3, 0x3B, 0x22, 0xC8, 0x00, 0x18,
-0xFC, 0xF3, 0x3B, 0x22, 0xD2, 0x82, 0x94,
-0xFC, 0xF3, 0x3B, 0x22, 0xEE, 0x00, 0x01,
-0xFC, 0xF3, 0x3B, 0x22, 0xF5, 0x00, 0x03,
-0xFC, 0xF3, 0x3B, 0x22, 0xF6, 0x00, 0x00,
-0xFC, 0xF3, 0x3B, 0x22, 0xF7, 0x00, 0x80,
-0xFC, 0xF3, 0x3B, 0x22, 0xF9, 0x08, 0x7F,
-0xFC, 0xF3, 0x3B, 0x22, 0xFA, 0x24, 0x8B,
-0xFC, 0xF3, 0x3B, 0x23, 0x03, 0x7D, 0xB9,
-0xFC, 0xF3, 0x3B, 0x23, 0x04, 0x01, 0xDE,
-0xFC, 0xF3, 0x3B, 0x23, 0x05, 0x20, 0x6C,
-0xFC, 0xF3, 0x3B, 0x23, 0x07, 0x00, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x08, 0x08, 0xFA,
-0xFC, 0xF3, 0x3B, 0x23, 0x09, 0x08, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x0A, 0x1A, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x0C, 0x00, 0xE9,
-0xFC, 0xF3, 0x3B, 0x23, 0x0D, 0x01, 0xD0,
-0xFC, 0xF3, 0x3B, 0x23, 0x10, 0x12, 0x80,
-0xFC, 0xF3, 0x3B, 0x23, 0x25, 0x28, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x26, 0x00, 0x38,
-0xFC, 0xF3, 0x3B, 0x23, 0x2F, 0x00, 0x04,
-0xFC, 0xF3, 0x3B, 0x23, 0x32, 0x01, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x33, 0x00, 0x0C,
-0xFC, 0xF3, 0x3B, 0x23, 0x37, 0x00, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x39, 0x00, 0x04,
-0xFC, 0xF3, 0x3B, 0x23, 0x48, 0x08, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x49, 0x08, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x50, 0x28, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x51, 0x28, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x52, 0x00, 0xC0,
-0xFC, 0xF3, 0x3B, 0x23, 0x56, 0x50, 0x28,
-0xFC, 0xF3, 0x3B, 0x23, 0x5A, 0x06, 0x80,
-0xFC, 0xF3, 0x3B, 0x23, 0x5B, 0x1E, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x60, 0x01, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x61, 0x10, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x64, 0x00, 0x40,
-0xFC, 0xF3, 0x3B, 0x23, 0x6E, 0x10, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x6F, 0x0E, 0x04,
-0xFC, 0xF3, 0x3B, 0x23, 0x70, 0x04, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x71, 0x05, 0x40,
-0xFC, 0xF3, 0x3B, 0x23, 0x73, 0x21, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x74, 0x1C, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x75, 0x12, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x77, 0x2C, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x7C, 0x58, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x80, 0x0A, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x81, 0x05, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x82, 0x03, 0x80,
-0xFC, 0xF3, 0x3B, 0x23, 0x83, 0x03, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x84, 0x00, 0x07,
-0xFC, 0xF3, 0x3B, 0x23, 0x86, 0x48, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x8B, 0x04, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x90, 0xB9, 0x88,
-0xFC, 0xF3, 0x3B, 0x23, 0x91, 0x87, 0x54,
-0xFC, 0xF3, 0x3B, 0x23, 0x92, 0x44, 0x43,
-0xFC, 0xF3, 0x3B, 0x23, 0x93, 0x32, 0x20,
-0xFC, 0xF3, 0x3B, 0x23, 0x9C, 0x70, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0xA6, 0x00, 0x02,
-0xFC, 0xF3, 0x3B, 0x23, 0xBD, 0x30, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0xC9, 0x16, 0x18,
-0xFC, 0xF3, 0x3B, 0x23, 0xCB, 0x10, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0xCD, 0x00, 0x04,
-0xFC, 0xF3, 0x3B, 0x23, 0xCF, 0x04, 0x80,
-0xFC, 0xF3, 0x3B, 0x23, 0xD0, 0x04, 0x80,
-0xFC, 0xF3, 0x3B, 0x23, 0xD1, 0x04, 0x80,
-0xFC, 0xF3, 0x3B, 0x23, 0xD2, 0x03, 0x80,
-0xFC, 0xF3, 0x3B, 0x23, 0xDB, 0x18, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0xDC, 0x39, 0xDE,
-0xFC, 0xF3, 0x3B, 0x23, 0xDF, 0x54, 0x44,
-0xFC, 0xF3, 0x3B, 0x23, 0xE0, 0x43, 0x34,
-0xFC, 0xF3, 0x3B, 0x23, 0xE1, 0x56, 0x66,
-0xFC, 0xF3, 0x3B, 0x23, 0xE2, 0x67, 0x68,
-0xFC, 0xF3, 0x3B, 0x23, 0xE5, 0x30, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0xE6, 0x60, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0xE7, 0x16, 0x3E,
-0xFC, 0xF3, 0x3B, 0x23, 0xE8, 0x16, 0xA2,
-0xFC, 0xF3, 0x3B, 0x23, 0xE9, 0x2C, 0xCC,
-0xFC, 0xF3, 0x3B, 0x23, 0xEA, 0x7F, 0xFF,
-0xFC, 0xF3, 0x3B, 0x23, 0xEB, 0x00, 0x04,
-0xFC, 0xF3, 0x3B, 0x23, 0xEC, 0x00, 0x08,
-0xFC, 0xF3, 0x3B, 0x23, 0xED, 0x01, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0xEE, 0x40, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0xF0, 0x40, 0x00,
-0xFC, 0xF3, 0x3B, 0x22, 0xFB, 0x00, 0x00
+ 0xFC, 0xF3, 0x68, 0x64, 0x04,
+ 0xFC, 0xF3, 0x3B, 0x3F, 0xE8, 0x00, 0x50,
+ 0xFC, 0xF3, 0x0D, 0x10, 0x00, 0x0C, 0x2C, 0x00,
+ 0xFC, 0xF3, 0x88,
+ 0x2E, 0xEC, 0xAC, 0x19, 0x00, 0x52, 0x47, 0xFF,
+ 0xF4, 0x19, 0x00, 0x6F, 0x1C, 0x99, 0x8F, 0x37,
+ 0x83, 0x01, 0x47, 0xC2, 0x97, 0x60, 0x00, 0xA5,
+ 0x62, 0xE2, 0xE6, 0x19, 0x00, 0xC4, 0x42, 0x66,
+ 0x67, 0x20, 0xCE, 0x0F, 0x0D, 0x00, 0xBC, 0x0D,
+ 0x00, 0xCD, 0x21, 0x0A, 0x0F, 0x6A, 0x64, 0xB4,
+ 0x80, 0x7A, 0x17, 0x68, 0x8A, 0xA6, 0x0E, 0x64,
+ 0x0F, 0x0E, 0x44, 0x0F, 0x10, 0x5B, 0x59, 0x90,
+ 0x7A, 0x1F, 0x80, 0x7A, 0x2A, 0x22, 0x6A, 0x0F,
+ 0x90, 0x7A, 0x2A, 0x34, 0x00, 0x0E, 0x1A, 0x61,
+ 0x0F,
+ 0xFC, 0xF3, 0x68, 0x64, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x3F, 0xA1, 0xA6, 0x06,
+ 0xFC, 0xF3, 0x3B, 0x3F, 0xB1, 0x50, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xC6, 0x00, 0x7D,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xC7, 0x00, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xC8, 0x00, 0x18,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xD2, 0x82, 0x94,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xEE, 0x00, 0x01,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xF5, 0x00, 0x03,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xF6, 0x00, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xF7, 0x00, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xF9, 0x08, 0x7F,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xFA, 0x24, 0x8B,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x02, 0x01, 0x01,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x03, 0x6D, 0x99,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x04, 0x03, 0xDE,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x05, 0x00, 0x28,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x07, 0x00, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x08, 0x08, 0xFA,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x09, 0x08, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x0A, 0x1A, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x0C, 0x01, 0x26,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x0D, 0x01, 0x08,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x10, 0x12, 0x80,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x25, 0x28, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x26, 0x00, 0x38,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x28, 0x7F, 0xFF,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x2F, 0x00, 0x08,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x32, 0x01, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x37, 0x00, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x39, 0x00, 0x04,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x3C, 0x00, 0x80,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x48, 0x08, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x49, 0x10, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x50, 0x00, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x51, 0x28, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x52, 0x00, 0xC0,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x56, 0x50, 0x28,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x6E, 0x0F, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x6F, 0x0E, 0x0E,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x70, 0x04, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x71, 0x05, 0x40,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x73, 0x21, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x74, 0x1C, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x75, 0x12, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x77, 0x2C, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x7C, 0x58, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x80, 0x0A, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x81, 0x05, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x82, 0x03, 0x80,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x83, 0x03, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x84, 0x00, 0x05,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x86, 0x48, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x8B, 0x04, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x8C, 0x08, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x90, 0x44, 0x44,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x91, 0x44, 0x44,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x92, 0x44, 0x44,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x93, 0x44, 0x44,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x9C, 0x70, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xA6, 0x00, 0x02,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xB3, 0x00, 0x18,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xB7, 0x00, 0x10,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xBD, 0x30, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xC9, 0x16, 0x18,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xCB, 0x10, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xCD, 0x00, 0x04,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xCF, 0x04, 0x80,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xD0, 0x04, 0x80,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xD1, 0x04, 0x80,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xD2, 0x03, 0x80,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xDB, 0x18, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xDC, 0x50, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xDF, 0x44, 0x44,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xE0, 0x44, 0x44,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xE1, 0x44, 0x44,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xE2, 0x44, 0x44,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xE5, 0x30, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xE6, 0x60, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xE7, 0x16, 0x3E,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xE8, 0x16, 0xA2,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xE9, 0x2C, 0xCC,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xEA, 0x7F, 0xFF,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xEB, 0x00, 0x02,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xEC, 0x00, 0x04,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xED, 0x01, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xF0, 0x40, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x33, 0x00, 0x0C,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xB4, 0x00, 0x06,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xEE, 0x30, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xFB, 0x00, 0x00
};
-unsigned char HF_cmd[] = {
+unsigned char HS_ExtraVol_cmd[] = {
/*0xC0,*/
-0xFC, 0xF3, 0x68, 0x64, 0x04,
-0xFC, 0xF3, 0x3B, 0x3F, 0xE8, 0x00, 0x50,
-0xFC, 0xF3, 0x0D, 0x10, 0x00, 0x0C, 0x2C, 0x00,
-0xFC, 0xF3, 0x88,
-0x2E, 0xEC, 0xAC, 0x19, 0x00, 0x52, 0x47, 0xFF,
-0xF4, 0x19, 0x00, 0x6F, 0x1C, 0x99, 0x8F, 0x37,
-0x83, 0x01, 0x47, 0xC2, 0x97, 0x60, 0x00, 0xA5,
-0x62, 0xE2, 0xE6, 0x19, 0x00, 0xC4, 0x42, 0x66,
-0x67, 0x20, 0xCE, 0x0F, 0x0D, 0x00, 0xBC, 0x0D,
-0x00, 0xCD, 0x21, 0x0A, 0x0F, 0x6A, 0x64, 0xB4,
-0x80, 0x7A, 0x17, 0x68, 0x8A, 0xA6, 0x0E, 0x64,
-0x0F, 0x0E, 0x44, 0x0F, 0x10, 0x5B, 0x59, 0x90,
-0x7A, 0x1F, 0x80, 0x7A, 0x2A, 0x22, 0x6A, 0x0F,
-0x90, 0x7A, 0x2A, 0x34, 0x00, 0x0E, 0x1A, 0x61,
-0x0F,
-0xFC, 0xF3, 0x68, 0x64, 0x00,
-0xFC, 0xF3, 0x3B, 0x3F, 0xA1, 0xA6, 0x06,
-0xFC, 0xF3, 0x3B, 0x3F, 0xB1, 0x50, 0x00,
-0xFC, 0xF3, 0x3B, 0x22, 0xB8, 0x1F, 0x40,
-0xFC, 0xF3, 0x3B, 0x22, 0xB9, 0x18, 0x00,
-0xFC, 0xF3, 0x3B, 0x22, 0xC6, 0x00, 0x7D,
-0xFC, 0xF3, 0x3B, 0x22, 0xC7, 0x00, 0x00,
-0xFC, 0xF3, 0x3B, 0x22, 0xC8, 0x00, 0x18,
-0xFC, 0xF3, 0x3B, 0x22, 0xD2, 0x82, 0x94,
-0xFC, 0xF3, 0x3B, 0x22, 0xEE, 0x00, 0x01,
-0xFC, 0xF3, 0x3B, 0x22, 0xF2, 0x00, 0x48,
-0xFC, 0xF3, 0x3B, 0x22, 0xF5, 0x00, 0x03,
-0xFC, 0xF3, 0x3B, 0x22, 0xF6, 0x00, 0x00,
-0xFC, 0xF3, 0x3B, 0x22, 0xF7, 0x00, 0x00,
-0xFC, 0xF3, 0x3B, 0x22, 0xF8, 0x80, 0x01,
-0xFC, 0xF3, 0x3B, 0x22, 0xF9, 0x08, 0x7F,
-0xFC, 0xF3, 0x3B, 0x22, 0xFA, 0x24, 0x8B,
-0xFC, 0xF3, 0x3B, 0x23, 0x03, 0x6D, 0xD9,
-0xFC, 0xF3, 0x3B, 0x23, 0x04, 0x03, 0xCF,
-0xFC, 0xF3, 0x3B, 0x23, 0x05, 0x00, 0x05,
-0xFC, 0xF3, 0x3B, 0x23, 0x07, 0x00, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x08, 0x04, 0x03,
-0xFC, 0xF3, 0x3B, 0x23, 0x09, 0x08, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x0A, 0x1A, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x0C, 0x01, 0x55,
-0xFC, 0xF3, 0x3B, 0x23, 0x0D, 0x0A, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x10, 0x12, 0x48,
-0xFC, 0xF3, 0x3B, 0x23, 0x11, 0x4E, 0xA9,
-0xFC, 0xF3, 0x3B, 0x23, 0x12, 0xB1, 0x5C,
-0xFC, 0xF3, 0x3B, 0x23, 0x13, 0x4E, 0xA9,
-0xFC, 0xF3, 0x3B, 0x23, 0x14, 0x95, 0x7C,
-0xFC, 0xF3, 0x3B, 0x23, 0x15, 0x5C, 0xB7,
-0xFC, 0xF3, 0x3B, 0x23, 0x16, 0x54, 0x2A,
-0xFC, 0xF3, 0x3B, 0x23, 0x17, 0xAB, 0xEF,
-0xFC, 0xF3, 0x3B, 0x23, 0x18, 0x54, 0x2A,
-0xFC, 0xF3, 0x3B, 0x23, 0x19, 0x84, 0x8D,
-0xFC, 0xF3, 0x3B, 0x23, 0x1A, 0x79, 0x55,
-0xFC, 0xF3, 0x3B, 0x23, 0x25, 0x58, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x26, 0x00, 0x38,
-0xFC, 0xF3, 0x3B, 0x23, 0x27, 0x20, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x2D, 0x00, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x2F, 0x00, 0x80,
-0xFC, 0xF3, 0x3B, 0x23, 0x32, 0x00, 0x30,
-0xFC, 0xF3, 0x3B, 0x23, 0x33, 0x00, 0x04,
-0xFC, 0xF3, 0x3B, 0x23, 0x37, 0xFF, 0xFF,
-0xFC, 0xF3, 0x3B, 0x23, 0x39, 0x00, 0x04,
-0xFC, 0xF3, 0x3B, 0x23, 0x3C, 0x01, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x3D, 0x12, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x3F, 0x00, 0x06,
-0xFC, 0xF3, 0x3B, 0x23, 0x41, 0xFF, 0xF3,
-0xFC, 0xF3, 0x3B, 0x23, 0x42, 0xFF, 0xF2,
-0xFC, 0xF3, 0x3B, 0x23, 0x43, 0x00, 0x04,
-0xFC, 0xF3, 0x3B, 0x23, 0x44, 0x00, 0x05,
-0xFC, 0xF3, 0x3B, 0x23, 0x48, 0x0B, 0x4D,
-0xFC, 0xF3, 0x3B, 0x23, 0x49, 0x0F, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x50, 0x34, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x51, 0x34, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x52, 0x00, 0x60,
-0xFC, 0xF3, 0x3B, 0x23, 0x53, 0x20, 0x10,
-0xFC, 0xF3, 0x3B, 0x23, 0x54, 0x1F, 0xF0,
-0xFC, 0xF3, 0x3B, 0x23, 0x56, 0x6A, 0x40,
-0xFC, 0xF3, 0x3B, 0x23, 0x5A, 0x18, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x5B, 0x20, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x5C, 0x40, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x5F, 0x00, 0x40,
-0xFC, 0xF3, 0x3B, 0x23, 0x64, 0x00, 0x3C,
-0xFC, 0xF3, 0x3B, 0x23, 0x65, 0x08, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x6E, 0x20, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x6F, 0x10, 0x06,
-0xFC, 0xF3, 0x3B, 0x23, 0x70, 0x00, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x71, 0x08, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x72, 0x18, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x73, 0x12, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x74, 0x15, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x75, 0x10, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x7A, 0x05, 0xAA,
-0xFC, 0xF3, 0x3B, 0x23, 0x7C, 0x70, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x7D, 0x70, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x7F, 0x70, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x80, 0x70, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x81, 0x50, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x82, 0x04, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x83, 0x05, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x84, 0x00, 0x07,
-0xFC, 0xF3, 0x3B, 0x23, 0x8C, 0x10, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x8D, 0x00, 0x04,
-0xFC, 0xF3, 0x3B, 0x23, 0x8E, 0x74, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x90, 0xA9, 0x98,
-0xFC, 0xF3, 0x3B, 0x23, 0x91, 0x75, 0x56,
-0xFC, 0xF3, 0x3B, 0x23, 0x92, 0x54, 0x43,
-0xFC, 0xF3, 0x3B, 0x23, 0x93, 0x33, 0x36,
-0xFC, 0xF3, 0x3B, 0x23, 0x96, 0x38, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x97, 0x20, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x9C, 0x40, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x9D, 0x08, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x9E, 0x08, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0xA5, 0x00, 0x07,
-0xFC, 0xF3, 0x3B, 0x23, 0xB3, 0x00, 0x0C,
-0xFC, 0xF3, 0x3B, 0x23, 0xB4, 0x00, 0x07,
-0xFC, 0xF3, 0x3B, 0x23, 0xB5, 0x60, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0xB7, 0x00, 0x20,
-0xFC, 0xF3, 0x3B, 0x23, 0xB8, 0x78, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0xB9, 0x24, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0xBA, 0x06, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0xBB, 0x0C, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0xBC, 0x00, 0xC0,
-0xFC, 0xF3, 0x3B, 0x23, 0xBD, 0x01, 0x80,
-0xFC, 0xF3, 0x3B, 0x23, 0xBE, 0x26, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0xCF, 0x20, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0xD0, 0x06, 0x80,
-0xFC, 0xF3, 0x3B, 0x23, 0xD1, 0x02, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0xD2, 0x18, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0xD5, 0x78, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0xDB, 0x18, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0xDC, 0x40, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0xDF, 0x77, 0x89,
-0xFC, 0xF3, 0x3B, 0x23, 0xE0, 0xAA, 0xAA,
-0xFC, 0xF3, 0x3B, 0x23, 0xE1, 0x98, 0x77,
-0xFC, 0xF3, 0x3B, 0x23, 0xE2, 0x54, 0x33,
-0xFC, 0xF3, 0x3B, 0x23, 0xE5, 0x30, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0xE6, 0x7F, 0xA0,
-0xFC, 0xF3, 0x3B, 0x23, 0xE7, 0x7F, 0xFF,
-0xFC, 0xF3, 0x3B, 0x23, 0xE8, 0x7F, 0xFF,
-0xFC, 0xF3, 0x3B, 0x23, 0xE9, 0x40, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0xEA, 0x7F, 0xFF,
-0xFC, 0xF3, 0x3B, 0x23, 0xEB, 0x00, 0x60,
-0xFC, 0xF3, 0x3B, 0x23, 0xEC, 0x00, 0x90,
-0xFC, 0xF3, 0x3B, 0x23, 0xED, 0x00, 0x60,
-0xFC, 0xF3, 0x3B, 0x23, 0xEE, 0x20, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0xEF, 0x40, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0xF0, 0x6A, 0x00,
-0xFC, 0xF3, 0x3B, 0x22, 0xFB, 0x00, 0x00
+ 0xFC, 0xF3, 0x68, 0x64, 0x04,
+ 0xFC, 0xF3, 0x3B, 0x3F, 0xE8, 0x00, 0x50,
+ 0xFC, 0xF3, 0x0D, 0x10, 0x00, 0x0C, 0x2C, 0x00,
+ 0xFC, 0xF3, 0x88,
+ 0x2E, 0xEC, 0xAC, 0x19, 0x00, 0x52, 0x47, 0xFF,
+ 0xF4, 0x19, 0x00, 0x6F, 0x1C, 0x99, 0x8F, 0x37,
+ 0x83, 0x01, 0x47, 0xC2, 0x97, 0x60, 0x00, 0xA5,
+ 0x62, 0xE2, 0xE6, 0x19, 0x00, 0xC4, 0x42, 0x66,
+ 0x67, 0x20, 0xCE, 0x0F, 0x0D, 0x00, 0xBC, 0x0D,
+ 0x00, 0xCD, 0x21, 0x0A, 0x0F, 0x6A, 0x64, 0xB4,
+ 0x80, 0x7A, 0x17, 0x68, 0x8A, 0xA6, 0x0E, 0x64,
+ 0x0F, 0x0E, 0x44, 0x0F, 0x10, 0x5B, 0x59, 0x90,
+ 0x7A, 0x1F, 0x80, 0x7A, 0x2A, 0x22, 0x6A, 0x0F,
+ 0x90, 0x7A, 0x2A, 0x34, 0x00, 0x0E, 0x1A, 0x61,
+ 0x0F,
+ 0xFC, 0xF3, 0x68, 0x64, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x3F, 0xA1, 0xA6, 0x06,
+ 0xFC, 0xF3, 0x3B, 0x3F, 0xB1, 0x50, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xC6, 0x00, 0x7D,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xC7, 0x00, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xC8, 0x00, 0x18,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xD2, 0x82, 0x94,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xEE, 0x00, 0x01,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xF5, 0x00, 0x03,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xF6, 0x00, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xF7, 0x00, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xF9, 0x08, 0x7F,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xFA, 0x24, 0x8B,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x02, 0x01, 0x01,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x04, 0x03, 0xDE,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x05, 0x00, 0x28,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x07, 0x00, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x08, 0x08, 0xFA,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x09, 0x08, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x0A, 0x1A, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x0D, 0x02, 0x50,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x10, 0x12, 0x80,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x25, 0x28, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x26, 0x00, 0x38,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x28, 0x7F, 0xFF,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x2F, 0x00, 0x08,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x32, 0x01, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x37, 0x00, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x39, 0x00, 0x04,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x3C, 0x00, 0x80,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x48, 0x08, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x49, 0x10, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x50, 0x00, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x51, 0x28, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x52, 0x00, 0xC0,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x56, 0x50, 0x28,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x6F, 0x0E, 0x0E,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x70, 0x04, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x71, 0x05, 0x40,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x73, 0x21, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x74, 0x1C, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x75, 0x12, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x77, 0x2C, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x7C, 0x58, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x80, 0x0A, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x81, 0x05, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x82, 0x03, 0x80,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x83, 0x03, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x86, 0x48, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x8B, 0x04, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x8C, 0x08, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x9C, 0x70, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xA6, 0x00, 0x02,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xB3, 0x00, 0x18,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xB4, 0x00, 0x09,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xB7, 0x00, 0x10,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xBD, 0x30, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xC9, 0x16, 0x18,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xCB, 0x10, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xCD, 0x00, 0x04,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xCF, 0x04, 0x80,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xD0, 0x04, 0x80,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xD1, 0x04, 0x80,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xD2, 0x03, 0x80,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xE5, 0x30, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xE6, 0x60, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xE7, 0x16, 0x3E,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xE8, 0x16, 0xA2,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xE9, 0x2C, 0xCC,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xEA, 0x7F, 0xFF,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xEB, 0x00, 0x04,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xEC, 0x00, 0x08,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xED, 0x01, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xF0, 0x40, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x03, 0x6D, 0x99,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x0C, 0x01, 0x26,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x6E, 0x0F, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x84, 0x00, 0x05,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x90, 0x44, 0x44,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x91, 0x44, 0x44,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x92, 0x44, 0x44,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x93, 0x44, 0x44,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xDB, 0x18, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xDC, 0x50, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xDF, 0x44, 0x44,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xE0, 0x44, 0x44,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xE1, 0x44, 0x44,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xE2, 0x44, 0x44,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x33, 0x00, 0x0C,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xEE, 0x30, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xFB, 0x00, 0x00
};
unsigned char HS_NS_cmd[] = {
/*0xC0,*/
-0xC0,
-0xFC, 0xF3, 0x68, 0x64, 0x04,
-0xFC, 0xF3, 0x3B, 0x3F, 0xE8, 0x00, 0x50,
-0xFC, 0xF3, 0x0D, 0x10, 0x00, 0x0C, 0x2C, 0x00,
-0xFC, 0xF3, 0x88,
-0x2E, 0xEC, 0xAC, 0x19, 0x00, 0x52, 0x47, 0xFF,
-0xF4, 0x19, 0x00, 0x6F, 0x1C, 0x99, 0x8F, 0x37,
-0x83, 0x01, 0x47, 0xC2, 0x97, 0x60, 0x00, 0xA5,
-0x62, 0xE2, 0xE6, 0x19, 0x00, 0xC4, 0x42, 0x66,
-0x67, 0x20, 0xCE, 0x0F, 0x0D, 0x00, 0xBC, 0x0D,
-0x00, 0xCD, 0x21, 0x0A, 0x0F, 0x6A, 0x64, 0xB4,
-0x80, 0x7A, 0x17, 0x68, 0x8A, 0xA6, 0x0E, 0x64,
-0x0F, 0x0E, 0x44, 0x0F, 0x10, 0x5B, 0x59, 0x90,
-0x7A, 0x1F, 0x80, 0x7A, 0x2A, 0x22, 0x6A, 0x0F,
-0x90, 0x7A, 0x2A, 0x34, 0x00, 0x0E, 0x1A, 0x61,
-0x0F,
-0xFC, 0xF3, 0x68, 0x64, 0x00,
-0xFC, 0xF3, 0x3B, 0x3F, 0xA1, 0xA6, 0x06,
-0xFC, 0xF3, 0x3B, 0x3F, 0xB1, 0x50, 0x00,
-0xFC, 0xF3, 0x3B, 0x22, 0xC6, 0x00, 0x7D,
-0xFC, 0xF3, 0x3B, 0x22, 0xC7, 0x00, 0x00,
-0xFC, 0xF3, 0x3B, 0x22, 0xC8, 0x00, 0x18,
-0xFC, 0xF3, 0x3B, 0x22, 0xD2, 0x82, 0x94,
-0xFC, 0xF3, 0x3B, 0x22, 0xEE, 0x00, 0x01,
-0xFC, 0xF3, 0x3B, 0x22, 0xF5, 0x00, 0x03,
-0xFC, 0xF3, 0x3B, 0x22, 0xF6, 0x00, 0x00,
-0xFC, 0xF3, 0x3B, 0x22, 0xF7, 0x00, 0x80,
-0xFC, 0xF3, 0x3B, 0x22, 0xF9, 0x08, 0x7F,
-0xFC, 0xF3, 0x3B, 0x22, 0xFA, 0x24, 0x8B,
-0xFC, 0xF3, 0x3B, 0x23, 0x03, 0x7D, 0xB9,
-0xFC, 0xF3, 0x3B, 0x23, 0x04, 0x01, 0xDE,
-0xFC, 0xF3, 0x3B, 0x23, 0x05, 0x20, 0x6C,
-0xFC, 0xF3, 0x3B, 0x23, 0x07, 0x00, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x08, 0x08, 0xFA,
-0xFC, 0xF3, 0x3B, 0x23, 0x09, 0x08, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x0A, 0x1A, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x0C, 0x00, 0xE9,
-0xFC, 0xF3, 0x3B, 0x23, 0x0D, 0x01, 0xD8,
-0xFC, 0xF3, 0x3B, 0x23, 0x10, 0x12, 0x80,
-0xFC, 0xF3, 0x3B, 0x23, 0x25, 0x28, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x26, 0x00, 0x38,
-0xFC, 0xF3, 0x3B, 0x23, 0x2F, 0x00, 0x04,
-0xFC, 0xF3, 0x3B, 0x23, 0x32, 0x01, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x33, 0x00, 0x0C,
-0xFC, 0xF3, 0x3B, 0x23, 0x37, 0x00, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x39, 0x00, 0x04,
-0xFC, 0xF3, 0x3B, 0x23, 0x48, 0x08, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x49, 0x08, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x50, 0x38, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x51, 0x28, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x52, 0x00, 0xC0,
-0xFC, 0xF3, 0x3B, 0x23, 0x56, 0x50, 0x32,
-0xFC, 0xF3, 0x3B, 0x23, 0x5A, 0x06, 0x80,
-0xFC, 0xF3, 0x3B, 0x23, 0x5B, 0x1E, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x60, 0x01, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x61, 0x10, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x64, 0x00, 0x40,
-0xFC, 0xF3, 0x3B, 0x23, 0x6E, 0x40, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x6F, 0x12, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x70, 0x04, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x71, 0x05, 0x40,
-0xFC, 0xF3, 0x3B, 0x23, 0x73, 0x21, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x74, 0x1C, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x75, 0x18, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x77, 0x2C, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x7B, 0x00, 0x08,
-0xFC, 0xF3, 0x3B, 0x23, 0x7C, 0x58, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x80, 0x0A, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x81, 0x05, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x82, 0x03, 0x80,
-0xFC, 0xF3, 0x3B, 0x23, 0x83, 0x03, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x84, 0x00, 0x07,
-0xFC, 0xF3, 0x3B, 0x23, 0x86, 0x48, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x8B, 0x01, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x90, 0xA9, 0x88,
-0xFC, 0xF3, 0x3B, 0x23, 0x91, 0x87, 0x54,
-0xFC, 0xF3, 0x3B, 0x23, 0x92, 0x44, 0x43,
-0xFC, 0xF3, 0x3B, 0x23, 0x93, 0x32, 0x21,
-0xFC, 0xF3, 0x3B, 0x23, 0x9C, 0x7F, 0xFF,
-0xFC, 0xF3, 0x3B, 0x23, 0xA6, 0x00, 0x02,
-0xFC, 0xF3, 0x3B, 0x23, 0xBD, 0x30, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0xC9, 0x16, 0x18,
-0xFC, 0xF3, 0x3B, 0x23, 0xCB, 0x10, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0xCD, 0x00, 0x04,
-0xFC, 0xF3, 0x3B, 0x23, 0xCF, 0x04, 0x80,
-0xFC, 0xF3, 0x3B, 0x23, 0xD0, 0x04, 0x80,
-0xFC, 0xF3, 0x3B, 0x23, 0xD1, 0x04, 0x80,
-0xFC, 0xF3, 0x3B, 0x23, 0xD2, 0x04, 0x80,
-0xFC, 0xF3, 0x3B, 0x23, 0xDB, 0x18, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0xDC, 0x39, 0xDE,
-0xFC, 0xF3, 0x3B, 0x23, 0xDF, 0x54, 0x44,
-0xFC, 0xF3, 0x3B, 0x23, 0xE0, 0x43, 0x34,
-0xFC, 0xF3, 0x3B, 0x23, 0xE1, 0x56, 0x66,
-0xFC, 0xF3, 0x3B, 0x23, 0xE2, 0x66, 0x66,
-0xFC, 0xF3, 0x3B, 0x23, 0xE5, 0x30, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0xE6, 0x60, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0xE7, 0x16, 0x3E,
-0xFC, 0xF3, 0x3B, 0x23, 0xE8, 0x16, 0xA2,
-0xFC, 0xF3, 0x3B, 0x23, 0xE9, 0x2C, 0xCC,
-0xFC, 0xF3, 0x3B, 0x23, 0xEA, 0x7F, 0xFF,
-0xFC, 0xF3, 0x3B, 0x23, 0xEB, 0x00, 0x04,
-0xFC, 0xF3, 0x3B, 0x23, 0xEC, 0x00, 0x08,
-0xFC, 0xF3, 0x3B, 0x23, 0xED, 0x01, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0xEE, 0x40, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0xF0, 0x40, 0x00,
-0xFC, 0xF3, 0x3B, 0x22, 0xFB, 0x00, 0x00
+ 0xFC, 0xF3, 0x68, 0x64, 0x04,
+ 0xFC, 0xF3, 0x3B, 0x3F, 0xE8, 0x00, 0x50,
+ 0xFC, 0xF3, 0x0D, 0x10, 0x00, 0x0C, 0x2C, 0x00,
+ 0xFC, 0xF3, 0x88,
+ 0x2E, 0xEC, 0xAC, 0x19, 0x00, 0x52, 0x47, 0xFF,
+ 0xF4, 0x19, 0x00, 0x6F, 0x1C, 0x99, 0x8F, 0x37,
+ 0x83, 0x01, 0x47, 0xC2, 0x97, 0x60, 0x00, 0xA5,
+ 0x62, 0xE2, 0xE6, 0x19, 0x00, 0xC4, 0x42, 0x66,
+ 0x67, 0x20, 0xCE, 0x0F, 0x0D, 0x00, 0xBC, 0x0D,
+ 0x00, 0xCD, 0x21, 0x0A, 0x0F, 0x6A, 0x64, 0xB4,
+ 0x80, 0x7A, 0x17, 0x68, 0x8A, 0xA6, 0x0E, 0x64,
+ 0x0F, 0x0E, 0x44, 0x0F, 0x10, 0x5B, 0x59, 0x90,
+ 0x7A, 0x1F, 0x80, 0x7A, 0x2A, 0x22, 0x6A, 0x0F,
+ 0x90, 0x7A, 0x2A, 0x34, 0x00, 0x0E, 0x1A, 0x61,
+ 0x0F,
+ 0xFC, 0xF3, 0x68, 0x64, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x3F, 0xA1, 0xA6, 0x06,
+ 0xFC, 0xF3, 0x3B, 0x3F, 0xB1, 0x50, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xBF, 0x15, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xC6, 0x00, 0x7D,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xC7, 0x00, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xC8, 0x00, 0x18,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xD2, 0x82, 0x94,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xEE, 0x00, 0x01,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xF5, 0x00, 0x03,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xF6, 0x00, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xF7, 0x00, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xFA, 0x24, 0x8B,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x04, 0x03, 0xDE,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x05, 0x20, 0x6C,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x07, 0x00, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x08, 0x08, 0xFA,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x09, 0x08, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x0A, 0x1A, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x10, 0x12, 0x80,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x25, 0x28, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x26, 0x00, 0x38,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x2F, 0x00, 0x04,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x32, 0x01, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x33, 0x00, 0x0C,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x37, 0x00, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x39, 0x00, 0x04,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x48, 0x08, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x49, 0x08, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x50, 0x1E, 0x80,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x52, 0x00, 0xC0,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x55, 0x6D, 0x80,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x56, 0x50, 0x28,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x5B, 0x20, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x60, 0x01, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x61, 0x10, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x64, 0x00, 0x40,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x6E, 0x10, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x70, 0x02, 0x80,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x71, 0x04, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x73, 0x1C, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x74, 0x18, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x75, 0x12, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x77, 0x2C, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x7C, 0x58, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x80, 0x0A, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x81, 0x05, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x82, 0x03, 0x80,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x83, 0x03, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x84, 0x00, 0x07,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x86, 0x48, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x8B, 0x04, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x9C, 0x70, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xA6, 0x00, 0x02,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xBD, 0x30, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xC9, 0x16, 0x18,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xCB, 0x10, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xCD, 0x00, 0x04,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xCF, 0x04, 0x80,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xD0, 0x04, 0x80,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xD1, 0x03, 0x20,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xD2, 0x03, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xE5, 0x30, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xE6, 0x60, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xE7, 0x16, 0x3E,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xE8, 0x16, 0xA2,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xE9, 0x2C, 0xCC,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xEA, 0x7F, 0xFF,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xEB, 0x00, 0x02,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xEC, 0x00, 0x04,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xED, 0x00, 0x30,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xEE, 0x40, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xEF, 0x58, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xF0, 0x74, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xF9, 0x08, 0x7F,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x28, 0x7F, 0xFF,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x03, 0x6D, 0x99,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x0C, 0x01, 0x26,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x0D, 0x01, 0x08,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x90, 0x44, 0x44,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x91, 0x44, 0x44,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x92, 0x44, 0x44,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x93, 0x44, 0x44,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xDB, 0x18, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xDC, 0x50, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xDF, 0x44, 0x44,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xE0, 0x44, 0x44,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xE1, 0x44, 0x44,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xE2, 0x44, 0x44,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xB3, 0x00, 0x08,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xB4, 0x00, 0x04,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x51, 0x20, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x6F, 0x0B, 0x10,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xFB, 0x00, 0x00
};
-unsigned char HS_ExtraVol_cmd[] = {
+unsigned char HF_cmd[] = {
/*0xC0,*/
-0xFC, 0xF3, 0x68, 0x64, 0x04,
-0xFC, 0xF3, 0x3B, 0x3F, 0xE8, 0x00, 0x50,
-0xFC, 0xF3, 0x0D, 0x10, 0x00, 0x0C, 0x2C, 0x00,
-0xFC, 0xF3, 0x88,
-0x2E, 0xEC, 0xAC, 0x19, 0x00, 0x52, 0x47, 0xFF,
-0xF4, 0x19, 0x00, 0x6F, 0x1C, 0x99, 0x8F, 0x37,
-0x83, 0x01, 0x47, 0xC2, 0x97, 0x60, 0x00, 0xA5,
-0x62, 0xE2, 0xE6, 0x19, 0x00, 0xC4, 0x42, 0x66,
-0x67, 0x20, 0xCE, 0x0F, 0x0D, 0x00, 0xBC, 0x0D,
-0x00, 0xCD, 0x21, 0x0A, 0x0F, 0x6A, 0x64, 0xB4,
-0x80, 0x7A, 0x17, 0x68, 0x8A, 0xA6, 0x0E, 0x64,
-0x0F, 0x0E, 0x44, 0x0F, 0x10, 0x5B, 0x59, 0x90,
-0x7A, 0x1F, 0x80, 0x7A, 0x2A, 0x22, 0x6A, 0x0F,
-0x90, 0x7A, 0x2A, 0x34, 0x00, 0x0E, 0x1A, 0x61,
-0x0F,
-0xFC, 0xF3, 0x68, 0x64, 0x00,
-0xFC, 0xF3, 0x3B, 0x3F, 0xA1, 0xA6, 0x06,
-0xFC, 0xF3, 0x3B, 0x3F, 0xB1, 0x50, 0x00,
-0xFC, 0xF3, 0x3B, 0x22, 0xC6, 0x00, 0x7D,
-0xFC, 0xF3, 0x3B, 0x22, 0xC7, 0x00, 0x00,
-0xFC, 0xF3, 0x3B, 0x22, 0xC8, 0x00, 0x18,
-0xFC, 0xF3, 0x3B, 0x22, 0xD2, 0x82, 0x94,
-0xFC, 0xF3, 0x3B, 0x22, 0xEE, 0x00, 0x01,
-0xFC, 0xF3, 0x3B, 0x22, 0xF5, 0x00, 0x03,
-0xFC, 0xF3, 0x3B, 0x22, 0xF6, 0x00, 0x00,
-0xFC, 0xF3, 0x3B, 0x22, 0xF7, 0x00, 0x80,
-0xFC, 0xF3, 0x3B, 0x22, 0xF9, 0x08, 0x7F,
-0xFC, 0xF3, 0x3B, 0x22, 0xFA, 0x24, 0x8B,
-0xFC, 0xF3, 0x3B, 0x23, 0x03, 0x7D, 0xB9,
-0xFC, 0xF3, 0x3B, 0x23, 0x04, 0x01, 0xDE,
-0xFC, 0xF3, 0x3B, 0x23, 0x05, 0x20, 0x6C,
-0xFC, 0xF3, 0x3B, 0x23, 0x07, 0x00, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x08, 0x08, 0xFA,
-0xFC, 0xF3, 0x3B, 0x23, 0x09, 0x08, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x0A, 0x1A, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x0C, 0x00, 0xE9,
-0xFC, 0xF3, 0x3B, 0x23, 0x0D, 0x05, 0xA0,
-0xFC, 0xF3, 0x3B, 0x23, 0x10, 0x12, 0x40,
-0xFC, 0xF3, 0x3B, 0x23, 0x25, 0x28, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x26, 0x00, 0x38,
-0xFC, 0xF3, 0x3B, 0x23, 0x2F, 0x00, 0x04,
-0xFC, 0xF3, 0x3B, 0x23, 0x32, 0x01, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x33, 0x00, 0x0C,
-0xFC, 0xF3, 0x3B, 0x23, 0x37, 0x00, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x39, 0x00, 0x04,
-0xFC, 0xF3, 0x3B, 0x23, 0x48, 0x08, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x49, 0x08, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x50, 0x28, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x51, 0x28, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x52, 0x00, 0xC0,
-0xFC, 0xF3, 0x3B, 0x23, 0x56, 0x50, 0x28,
-0xFC, 0xF3, 0x3B, 0x23, 0x5A, 0x06, 0x80,
-0xFC, 0xF3, 0x3B, 0x23, 0x5B, 0x1E, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x60, 0x01, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x61, 0x10, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x64, 0x00, 0x40,
-0xFC, 0xF3, 0x3B, 0x23, 0x6E, 0x10, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x6F, 0x0E, 0x04,
-0xFC, 0xF3, 0x3B, 0x23, 0x70, 0x04, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x71, 0x05, 0x40,
-0xFC, 0xF3, 0x3B, 0x23, 0x73, 0x21, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x74, 0x1C, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x75, 0x12, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x77, 0x2C, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x7C, 0x58, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x80, 0x0A, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x81, 0x05, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x82, 0x03, 0x80,
-0xFC, 0xF3, 0x3B, 0x23, 0x83, 0x03, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x84, 0x00, 0x07,
-0xFC, 0xF3, 0x3B, 0x23, 0x86, 0x48, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x8B, 0x04, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x90, 0xB9, 0x88,
-0xFC, 0xF3, 0x3B, 0x23, 0x91, 0x87, 0x54,
-0xFC, 0xF3, 0x3B, 0x23, 0x92, 0x44, 0x43,
-0xFC, 0xF3, 0x3B, 0x23, 0x93, 0x32, 0x20,
-0xFC, 0xF3, 0x3B, 0x23, 0x9C, 0x70, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0xA6, 0x00, 0x02,
-0xFC, 0xF3, 0x3B, 0x23, 0xBD, 0x30, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0xC9, 0x16, 0x18,
-0xFC, 0xF3, 0x3B, 0x23, 0xCB, 0x10, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0xCD, 0x00, 0x04,
-0xFC, 0xF3, 0x3B, 0x23, 0xCF, 0x04, 0x80,
-0xFC, 0xF3, 0x3B, 0x23, 0xD0, 0x04, 0x80,
-0xFC, 0xF3, 0x3B, 0x23, 0xD1, 0x04, 0x80,
-0xFC, 0xF3, 0x3B, 0x23, 0xD2, 0x03, 0x80,
-0xFC, 0xF3, 0x3B, 0x23, 0xDB, 0x18, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0xDC, 0x39, 0xDE,
-0xFC, 0xF3, 0x3B, 0x23, 0xDF, 0x65, 0x55,
-0xFC, 0xF3, 0x3B, 0x23, 0xE0, 0x54, 0x45,
-0xFC, 0xF3, 0x3B, 0x23, 0xE1, 0x54, 0x43,
-0xFC, 0xF3, 0x3B, 0x23, 0xE2, 0x33, 0x22,
-0xFC, 0xF3, 0x3B, 0x23, 0xE5, 0x30, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0xE6, 0x60, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0xE7, 0x16, 0x3E,
-0xFC, 0xF3, 0x3B, 0x23, 0xE8, 0x16, 0xA2,
-0xFC, 0xF3, 0x3B, 0x23, 0xE9, 0x2C, 0xCC,
-0xFC, 0xF3, 0x3B, 0x23, 0xEA, 0x7F, 0xFF,
-0xFC, 0xF3, 0x3B, 0x23, 0xEB, 0x00, 0x10,
-0xFC, 0xF3, 0x3B, 0x23, 0xEC, 0x00, 0x20,
-0xFC, 0xF3, 0x3B, 0x23, 0xED, 0x01, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0xEE, 0x40, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0xF0, 0x40, 0x00,
-0xFC, 0xF3, 0x3B, 0x22, 0xFB, 0x00, 0x00
+ 0xFC, 0xF3, 0x68, 0x64, 0x04,
+ 0xFC, 0xF3, 0x3B, 0x3F, 0xE8, 0x00, 0x50,
+ 0xFC, 0xF3, 0x0D, 0x10, 0x00, 0x0C, 0x2C, 0x00,
+ 0xFC, 0xF3, 0x88,
+ 0x2E, 0xEC, 0xAC, 0x19, 0x00, 0x52, 0x47, 0xFF,
+ 0xF4, 0x19, 0x00, 0x6F, 0x1C, 0x99, 0x8F, 0x37,
+ 0x83, 0x01, 0x47, 0xC2, 0x97, 0x60, 0x00, 0xA5,
+ 0x62, 0xE2, 0xE6, 0x19, 0x00, 0xC4, 0x42, 0x66,
+ 0x67, 0x20, 0xCE, 0x0F, 0x0D, 0x00, 0xBC, 0x0D,
+ 0x00, 0xCD, 0x21, 0x0A, 0x0F, 0x6A, 0x64, 0xB4,
+ 0x80, 0x7A, 0x17, 0x68, 0x8A, 0xA6, 0x0E, 0x64,
+ 0x0F, 0x0E, 0x44, 0x0F, 0x10, 0x5B, 0x59, 0x90,
+ 0x7A, 0x1F, 0x80, 0x7A, 0x2A, 0x22, 0x6A, 0x0F,
+ 0x90, 0x7A, 0x2A, 0x34, 0x00, 0x0E, 0x1A, 0x61,
+ 0x0F,
+ 0xFC, 0xF3, 0x68, 0x64, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x3F, 0xA1, 0xA6, 0x06,
+ 0xFC, 0xF3, 0x3B, 0x3F, 0xB1, 0x50, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xB8, 0x1F, 0x40,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xB9, 0x18, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xC6, 0x00, 0x7D,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xC7, 0x00, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xC8, 0x00, 0x18,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xD2, 0x82, 0x94,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xEE, 0x00, 0x01,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xF2, 0x00, 0x48,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xF5, 0x00, 0x03,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xF6, 0x00, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xF7, 0x00, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xF8, 0x80, 0x01,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xF9, 0x08, 0x7F,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xFA, 0x24, 0x8B,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x03, 0x6D, 0xD9,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x04, 0x03, 0xCF,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x05, 0x00, 0x2D,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x07, 0x00, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x08, 0x04, 0x03,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x09, 0x08, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x0A, 0x1A, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x0C, 0x01, 0x70,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x0D, 0x01, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x10, 0x12, 0x12,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x11, 0x4E, 0xA9,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x12, 0xB1, 0x5C,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x13, 0x4E, 0xA9,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x14, 0x95, 0x7C,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x15, 0x5C, 0xB7,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x16, 0x54, 0x2A,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x17, 0xAB, 0xEF,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x18, 0x54, 0x2A,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x19, 0x84, 0x8D,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x1A, 0x79, 0x55,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x25, 0x58, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x26, 0x00, 0x38,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x27, 0x20, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x28, 0x7F, 0xFF,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x2D, 0x00, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x2F, 0x00, 0x80,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x32, 0x00, 0x30,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x33, 0x00, 0x04,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x37, 0xFF, 0xFF,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x39, 0x00, 0x04,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x3C, 0x01, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x3D, 0x12, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x3F, 0x00, 0x06,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x41, 0xFF, 0xF3,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x42, 0xFF, 0xF2,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x43, 0x00, 0x04,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x44, 0x00, 0x05,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x48, 0x0B, 0x4D,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x49, 0x03, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x50, 0x34, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x51, 0x34, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x52, 0x00, 0x60,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x53, 0x20, 0x10,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x54, 0x1F, 0xF0,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x56, 0x6A, 0x40,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x5A, 0x09, 0x80,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x5B, 0x18, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x5C, 0x40, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x5F, 0x00, 0x40,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x64, 0x00, 0x3C,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x65, 0x08, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x6E, 0x20, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x6F, 0x10, 0x06,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x70, 0x00, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x71, 0x08, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x72, 0x18, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x73, 0x12, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x74, 0x15, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x75, 0x10, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x7A, 0x05, 0xAA,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x7C, 0x70, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x7D, 0x70, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x7F, 0x70, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x80, 0x70, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x81, 0x50, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x82, 0x04, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x83, 0x05, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x84, 0x00, 0x07,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x8C, 0x10, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x8D, 0x00, 0x04,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x8E, 0x74, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x90, 0xA9, 0x98,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x91, 0x75, 0x56,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x92, 0x54, 0x43,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x93, 0x33, 0x36,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x96, 0x24, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x97, 0x20, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x9C, 0x40, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x9D, 0x08, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x9E, 0x08, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xA5, 0x00, 0x07,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xB3, 0x00, 0x0C,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xB4, 0x00, 0x09,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xB5, 0x60, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xB7, 0x00, 0x20,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xB8, 0x78, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xB9, 0x24, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xBA, 0x06, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xBB, 0x0C, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xBC, 0x00, 0xC0,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xBD, 0x01, 0x80,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xBE, 0x26, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xCF, 0x20, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xD0, 0x06, 0x80,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xD1, 0x02, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xD2, 0x18, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xD5, 0x78, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xDB, 0x28, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xDC, 0x50, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xE5, 0x30, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xE6, 0x7F, 0xA0,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xE7, 0x7F, 0xFF,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xE8, 0x7F, 0xFF,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xE9, 0x40, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xEA, 0x7F, 0xFF,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xEB, 0x00, 0x60,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xEC, 0x00, 0x90,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xED, 0x00, 0x60,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xEE, 0x20, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xEF, 0x40, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xF0, 0x6A, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xFB, 0x00, 0x00
};
unsigned char HF_ExtraVol_cmd[] = {
/*0xC0,*/
-0xFC, 0xF3, 0x68, 0x64, 0x04,
-0xFC, 0xF3, 0x3B, 0x3F, 0xE8, 0x00, 0x50,
-0xFC, 0xF3, 0x0D, 0x10, 0x00, 0x0C, 0x2C, 0x00,
-0xFC, 0xF3, 0x88,
-0x2E, 0xEC, 0xAC, 0x19, 0x00, 0x52, 0x47, 0xFF,
-0xF4, 0x19, 0x00, 0x6F, 0x1C, 0x99, 0x8F, 0x37,
-0x83, 0x01, 0x47, 0xC2, 0x97, 0x60, 0x00, 0xA5,
-0x62, 0xE2, 0xE6, 0x19, 0x00, 0xC4, 0x42, 0x66,
-0x67, 0x20, 0xCE, 0x0F, 0x0D, 0x00, 0xBC, 0x0D,
-0x00, 0xCD, 0x21, 0x0A, 0x0F, 0x6A, 0x64, 0xB4,
-0x80, 0x7A, 0x17, 0x68, 0x8A, 0xA6, 0x0E, 0x64,
-0x0F, 0x0E, 0x44, 0x0F, 0x10, 0x5B, 0x59, 0x90,
-0x7A, 0x1F, 0x80, 0x7A, 0x2A, 0x22, 0x6A, 0x0F,
-0x90, 0x7A, 0x2A, 0x34, 0x00, 0x0E, 0x1A, 0x61,
-0x0F,
-0xFC, 0xF3, 0x68, 0x64, 0x00,
-0xFC, 0xF3, 0x3B, 0x3F, 0xA1, 0xA6, 0x06,
-0xFC, 0xF3, 0x3B, 0x3F, 0xB1, 0x50, 0x00,
-0xFC, 0xF3, 0x3B, 0x22, 0xB8, 0x1F, 0x40,
-0xFC, 0xF3, 0x3B, 0x22, 0xB9, 0x18, 0x00,
-0xFC, 0xF3, 0x3B, 0x22, 0xC6, 0x00, 0x7D,
-0xFC, 0xF3, 0x3B, 0x22, 0xC7, 0x00, 0x00,
-0xFC, 0xF3, 0x3B, 0x22, 0xC8, 0x00, 0x18,
-0xFC, 0xF3, 0x3B, 0x22, 0xD2, 0x82, 0x94,
-0xFC, 0xF3, 0x3B, 0x22, 0xEE, 0x00, 0x01,
-0xFC, 0xF3, 0x3B, 0x22, 0xF2, 0x00, 0x48,
-0xFC, 0xF3, 0x3B, 0x22, 0xF5, 0x00, 0x03,
-0xFC, 0xF3, 0x3B, 0x22, 0xF6, 0x00, 0x00,
-0xFC, 0xF3, 0x3B, 0x22, 0xF7, 0x00, 0x00,
-0xFC, 0xF3, 0x3B, 0x22, 0xF8, 0x80, 0x01,
-0xFC, 0xF3, 0x3B, 0x22, 0xF9, 0x08, 0x7F,
-0xFC, 0xF3, 0x3B, 0x22, 0xFA, 0x24, 0x8B,
-0xFC, 0xF3, 0x3B, 0x23, 0x03, 0x6D, 0xD9,
-0xFC, 0xF3, 0x3B, 0x23, 0x04, 0x03, 0xCF,
-0xFC, 0xF3, 0x3B, 0x23, 0x05, 0x00, 0x05,
-0xFC, 0xF3, 0x3B, 0x23, 0x07, 0x00, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x08, 0x04, 0x03,
-0xFC, 0xF3, 0x3B, 0x23, 0x09, 0x08, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x0A, 0x1A, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x0C, 0x01, 0x30,
-0xFC, 0xF3, 0x3B, 0x23, 0x0D, 0x08, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x10, 0x12, 0x40,
-0xFC, 0xF3, 0x3B, 0x23, 0x11, 0x4E, 0xA9,
-0xFC, 0xF3, 0x3B, 0x23, 0x12, 0xB1, 0x5C,
-0xFC, 0xF3, 0x3B, 0x23, 0x13, 0x4E, 0xA9,
-0xFC, 0xF3, 0x3B, 0x23, 0x14, 0x95, 0x7C,
-0xFC, 0xF3, 0x3B, 0x23, 0x15, 0x5C, 0xB7,
-0xFC, 0xF3, 0x3B, 0x23, 0x16, 0x54, 0x2A,
-0xFC, 0xF3, 0x3B, 0x23, 0x17, 0xAB, 0xEF,
-0xFC, 0xF3, 0x3B, 0x23, 0x18, 0x54, 0x2A,
-0xFC, 0xF3, 0x3B, 0x23, 0x19, 0x84, 0x8D,
-0xFC, 0xF3, 0x3B, 0x23, 0x1A, 0x79, 0x55,
-0xFC, 0xF3, 0x3B, 0x23, 0x25, 0x58, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x26, 0x00, 0x38,
-0xFC, 0xF3, 0x3B, 0x23, 0x27, 0x20, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x2D, 0x00, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x2F, 0x00, 0x80,
-0xFC, 0xF3, 0x3B, 0x23, 0x32, 0x00, 0x30,
-0xFC, 0xF3, 0x3B, 0x23, 0x33, 0x00, 0x04,
-0xFC, 0xF3, 0x3B, 0x23, 0x37, 0xFF, 0xFF,
-0xFC, 0xF3, 0x3B, 0x23, 0x39, 0x00, 0x04,
-0xFC, 0xF3, 0x3B, 0x23, 0x3C, 0x01, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x3D, 0x12, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x3F, 0x00, 0x06,
-0xFC, 0xF3, 0x3B, 0x23, 0x41, 0xFF, 0xF3,
-0xFC, 0xF3, 0x3B, 0x23, 0x42, 0xFF, 0xF2,
-0xFC, 0xF3, 0x3B, 0x23, 0x43, 0x00, 0x04,
-0xFC, 0xF3, 0x3B, 0x23, 0x44, 0x00, 0x05,
-0xFC, 0xF3, 0x3B, 0x23, 0x48, 0x0B, 0x4D,
-0xFC, 0xF3, 0x3B, 0x23, 0x49, 0x0F, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x50, 0x34, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x51, 0x34, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x52, 0x00, 0x60,
-0xFC, 0xF3, 0x3B, 0x23, 0x53, 0x20, 0x10,
-0xFC, 0xF3, 0x3B, 0x23, 0x54, 0x1F, 0xF0,
-0xFC, 0xF3, 0x3B, 0x23, 0x56, 0x6A, 0x40,
-0xFC, 0xF3, 0x3B, 0x23, 0x5A, 0x18, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x5B, 0x20, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x5C, 0x40, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x5F, 0x00, 0x40,
-0xFC, 0xF3, 0x3B, 0x23, 0x64, 0x00, 0x3C,
-0xFC, 0xF3, 0x3B, 0x23, 0x65, 0x08, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x6E, 0x20, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x6F, 0x10, 0x06,
-0xFC, 0xF3, 0x3B, 0x23, 0x70, 0x00, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x71, 0x08, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x72, 0x18, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x73, 0x12, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x74, 0x15, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x75, 0x10, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x7A, 0x05, 0xAA,
-0xFC, 0xF3, 0x3B, 0x23, 0x7C, 0x70, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x7D, 0x70, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x7F, 0x70, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x80, 0x70, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x81, 0x50, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x82, 0x04, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x83, 0x05, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x84, 0x00, 0x07,
-0xFC, 0xF3, 0x3B, 0x23, 0x8C, 0x10, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x8D, 0x00, 0x04,
-0xFC, 0xF3, 0x3B, 0x23, 0x8E, 0x74, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x90, 0xA9, 0x98,
-0xFC, 0xF3, 0x3B, 0x23, 0x91, 0x75, 0x56,
-0xFC, 0xF3, 0x3B, 0x23, 0x92, 0x54, 0x43,
-0xFC, 0xF3, 0x3B, 0x23, 0x93, 0x33, 0x36,
-0xFC, 0xF3, 0x3B, 0x23, 0x96, 0x38, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x97, 0x20, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x9C, 0x40, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x9D, 0x08, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x9E, 0x08, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0xA5, 0x00, 0x07,
-0xFC, 0xF3, 0x3B, 0x23, 0xB3, 0x00, 0x0C,
-0xFC, 0xF3, 0x3B, 0x23, 0xB4, 0x00, 0x07,
-0xFC, 0xF3, 0x3B, 0x23, 0xB5, 0x60, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0xB7, 0x00, 0x20,
-0xFC, 0xF3, 0x3B, 0x23, 0xB8, 0x78, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0xB9, 0x24, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0xBA, 0x06, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0xBB, 0x0C, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0xBC, 0x00, 0xC0,
-0xFC, 0xF3, 0x3B, 0x23, 0xBD, 0x01, 0x80,
-0xFC, 0xF3, 0x3B, 0x23, 0xBE, 0x26, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0xCF, 0x20, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0xD0, 0x06, 0x80,
-0xFC, 0xF3, 0x3B, 0x23, 0xD1, 0x02, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0xD2, 0x18, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0xD5, 0x78, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0xDB, 0x18, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0xDC, 0x40, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0xDF, 0xFD, 0xA8,
-0xFC, 0xF3, 0x3B, 0x23, 0xE0, 0x76, 0x54,
-0xFC, 0xF3, 0x3B, 0x23, 0xE1, 0x32, 0x10,
-0xFC, 0xF3, 0x3B, 0x23, 0xE2, 0x00, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0xE5, 0x30, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0xE6, 0x7F, 0xA0,
-0xFC, 0xF3, 0x3B, 0x23, 0xE7, 0x7F, 0xFF,
-0xFC, 0xF3, 0x3B, 0x23, 0xE8, 0x7F, 0xFF,
-0xFC, 0xF3, 0x3B, 0x23, 0xE9, 0x40, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0xEA, 0x7F, 0xFF,
-0xFC, 0xF3, 0x3B, 0x23, 0xEB, 0x00, 0x60,
-0xFC, 0xF3, 0x3B, 0x23, 0xEC, 0x00, 0x90,
-0xFC, 0xF3, 0x3B, 0x23, 0xED, 0x00, 0x60,
-0xFC, 0xF3, 0x3B, 0x23, 0xEE, 0x20, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0xEF, 0x40, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0xF0, 0x6A, 0x00,
-0xFC, 0xF3, 0x3B, 0x22, 0xFB, 0x00, 0x00
+ 0xFC, 0xF3, 0x68, 0x64, 0x04,
+ 0xFC, 0xF3, 0x3B, 0x3F, 0xE8, 0x00, 0x50,
+ 0xFC, 0xF3, 0x0D, 0x10, 0x00, 0x0C, 0x2C, 0x00,
+ 0xFC, 0xF3, 0x88,
+ 0x2E, 0xEC, 0xAC, 0x19, 0x00, 0x52, 0x47, 0xFF,
+ 0xF4, 0x19, 0x00, 0x6F, 0x1C, 0x99, 0x8F, 0x37,
+ 0x83, 0x01, 0x47, 0xC2, 0x97, 0x60, 0x00, 0xA5,
+ 0x62, 0xE2, 0xE6, 0x19, 0x00, 0xC4, 0x42, 0x66,
+ 0x67, 0x20, 0xCE, 0x0F, 0x0D, 0x00, 0xBC, 0x0D,
+ 0x00, 0xCD, 0x21, 0x0A, 0x0F, 0x6A, 0x64, 0xB4,
+ 0x80, 0x7A, 0x17, 0x68, 0x8A, 0xA6, 0x0E, 0x64,
+ 0x0F, 0x0E, 0x44, 0x0F, 0x10, 0x5B, 0x59, 0x90,
+ 0x7A, 0x1F, 0x80, 0x7A, 0x2A, 0x22, 0x6A, 0x0F,
+ 0x90, 0x7A, 0x2A, 0x34, 0x00, 0x0E, 0x1A, 0x61,
+ 0x0F,
+ 0xFC, 0xF3, 0x68, 0x64, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x3F, 0xA1, 0xA6, 0x06,
+ 0xFC, 0xF3, 0x3B, 0x3F, 0xB1, 0x50, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xB8, 0x1F, 0x40,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xB9, 0x18, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xC6, 0x00, 0x7D,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xC7, 0x00, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xC8, 0x00, 0x18,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xD2, 0x82, 0x94,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xEE, 0x00, 0x01,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xF2, 0x00, 0x48,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xF5, 0x00, 0x03,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xF6, 0x00, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xF7, 0x00, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xF8, 0x80, 0x01,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xF9, 0x08, 0x7F,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xFA, 0x24, 0x8B,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x03, 0x6D, 0xD9,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x04, 0x03, 0xCF,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x05, 0x00, 0x2D,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x07, 0x00, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x08, 0x04, 0x03,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x09, 0x08, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x0A, 0x1A, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x0C, 0x01, 0x70,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x0D, 0x01, 0x05,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x10, 0x12, 0x12,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x11, 0x4E, 0xA9,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x12, 0xB1, 0x5C,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x13, 0x4E, 0xA9,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x14, 0x95, 0x7C,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x15, 0x5C, 0xB7,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x16, 0x54, 0x2A,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x17, 0xAB, 0xEF,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x18, 0x54, 0x2A,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x19, 0x84, 0x8D,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x1A, 0x79, 0x55,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x25, 0x58, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x26, 0x00, 0x38,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x27, 0x20, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x28, 0x7F, 0xFF,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x2D, 0x00, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x2F, 0x00, 0x80,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x32, 0x00, 0x30,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x33, 0x00, 0x04,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x37, 0xFF, 0xFF,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x39, 0x00, 0x04,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x3C, 0x01, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x3D, 0x12, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x3F, 0x00, 0x06,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x41, 0xFF, 0xF3,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x42, 0xFF, 0xF2,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x43, 0x00, 0x04,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x44, 0x00, 0x05,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x48, 0x0B, 0x4D,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x49, 0x03, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x50, 0x34, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x51, 0x34, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x52, 0x00, 0x60,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x53, 0x20, 0x10,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x54, 0x1F, 0xF0,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x56, 0x6A, 0x40,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x5A, 0x09, 0x80,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x5B, 0x18, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x5C, 0x40, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x5F, 0x00, 0x40,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x64, 0x00, 0x3C,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x65, 0x08, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x6E, 0x20, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x6F, 0x10, 0x06,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x70, 0x00, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x71, 0x08, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x72, 0x18, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x73, 0x12, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x74, 0x15, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x75, 0x10, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x7A, 0x05, 0xAA,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x7C, 0x70, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x7D, 0x70, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x7F, 0x70, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x80, 0x70, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x81, 0x50, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x82, 0x04, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x83, 0x05, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x84, 0x00, 0x07,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x8C, 0x10, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x8D, 0x00, 0x04,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x8E, 0x74, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x90, 0xA9, 0x98,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x91, 0x75, 0x56,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x92, 0x54, 0x43,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x93, 0x33, 0x36,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x96, 0x24, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x97, 0x20, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x9C, 0x40, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x9D, 0x08, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x9E, 0x08, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xA5, 0x00, 0x07,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xB3, 0x00, 0x0C,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xB4, 0x00, 0x09,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xB5, 0x60, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xB7, 0x00, 0x20,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xB8, 0x78, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xB9, 0x24, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xBA, 0x06, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xBB, 0x0C, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xBC, 0x00, 0xC0,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xBD, 0x01, 0x80,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xBE, 0x26, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xCF, 0x20, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xD0, 0x06, 0x80,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xD1, 0x02, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xD2, 0x18, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xD5, 0x78, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xDB, 0x28, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xDC, 0x50, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xE5, 0x30, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xE6, 0x7F, 0xA0,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xE7, 0x7F, 0xFF,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xE8, 0x7F, 0xFF,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xE9, 0x40, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xEA, 0x7F, 0xFF,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xEB, 0x00, 0x60,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xEC, 0x00, 0x90,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xED, 0x00, 0x60,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xEE, 0x20, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xEF, 0x40, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xF0, 0x6A, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xFB, 0x00, 0x00
};
unsigned char EP_cmd[] = {
/*0xC0,*/
-0xFC, 0xF3, 0x3B, 0x22, 0xC0, 0x00, 0x00,
-0xFC, 0xF3, 0x3B, 0x22, 0xC1, 0x00, 0x01,
-0xFC, 0xF3, 0x3B, 0x22, 0xC2, 0x00, 0x02,
-0xFC, 0xF3, 0x3B, 0x22, 0xC3, 0x00, 0x02,
-0xFC, 0xF3, 0x3B, 0x22, 0xC6, 0x00, 0x7D,
-0xFC, 0xF3, 0x3B, 0x22, 0xC7, 0x00, 0x00,
-0xFC, 0xF3, 0x3B, 0x22, 0xC8, 0x00, 0x18,
-0xFC, 0xF3, 0x3B, 0x22, 0xD2, 0x82, 0x94,
-0xFC, 0xF3, 0x3B, 0x22, 0xEE, 0x00, 0x01,
-0xFC, 0xF3, 0x3B, 0x22, 0xF5, 0x00, 0x03,
-0xFC, 0xF3, 0x3B, 0x22, 0xF6, 0x00, 0x00,
-0xFC, 0xF3, 0x3B, 0x22, 0xF8, 0x80, 0x01,
-0xFC, 0xF3, 0x3B, 0x22, 0xF9, 0x08, 0x7F,
-0xFC, 0xF3, 0x3B, 0x22, 0xFA, 0x24, 0x8B,
-0xFC, 0xF3, 0x3B, 0x23, 0x07, 0x00, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x0A, 0x1A, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x0C, 0x00, 0xB8,
-0xFC, 0xF3, 0x3B, 0x23, 0x0D, 0x02, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x65, 0x08, 0x00,
-0xFC, 0xF3, 0x3B, 0x22, 0xFB, 0x00, 0x00
+ 0xFC, 0xF3, 0x3B, 0x22, 0xC2, 0x00, 0x02,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xC3, 0x00, 0x02,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xC6, 0x00, 0x7D,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xC7, 0x00, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xC8, 0x00, 0x18,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xD2, 0x82, 0x94,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xEE, 0x00, 0x01,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xF5, 0x00, 0x03,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xF6, 0x00, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xF9, 0x08, 0x7F,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xFA, 0x24, 0x8B,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x02, 0x00, 0x01,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x03, 0x01, 0x01,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x04, 0x00, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x05, 0x00, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x08, 0x00, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x09, 0x08, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x0A, 0x1A, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x0C, 0x01, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x0D, 0x01, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x10, 0x12, 0x80,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x48, 0x08, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x49, 0x00, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x50, 0x38, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x51, 0x28, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x6E, 0x7F, 0xFF,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xED, 0x02, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xEE, 0x40, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0xF0, 0x30, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xFB, 0x00, 0x00
};
unsigned char BT_SCO_cmd[] = {
/*0xC0,*/
-0xFC, 0xF3, 0x3B, 0x22, 0xC0, 0x00, 0x00,
-0xFC, 0xF3, 0x3B, 0x22, 0xC1, 0x00, 0x01,
-0xFC, 0xF3, 0x3B, 0x22, 0xC2, 0x00, 0x02,
-0xFC, 0xF3, 0x3B, 0x22, 0xC3, 0x00, 0x02,
-0xFC, 0xF3, 0x3B, 0x22, 0xC6, 0x00, 0x7D,
-0xFC, 0xF3, 0x3B, 0x22, 0xC7, 0x00, 0x00,
-0xFC, 0xF3, 0x3B, 0x22, 0xC8, 0x00, 0x18,
-0xFC, 0xF3, 0x3B, 0x22, 0xD2, 0x82, 0x94,
-0xFC, 0xF3, 0x3B, 0x22, 0xEE, 0x00, 0x01,
-0xFC, 0xF3, 0x3B, 0x22, 0xF5, 0x00, 0x03,
-0xFC, 0xF3, 0x3B, 0x22, 0xF6, 0x00, 0x00,
-0xFC, 0xF3, 0x3B, 0x22, 0xF8, 0x80, 0x01,
-0xFC, 0xF3, 0x3B, 0x22, 0xF9, 0x08, 0x7F,
-0xFC, 0xF3, 0x3B, 0x22, 0xFA, 0x24, 0x8B,
-0xFC, 0xF3, 0x3B, 0x23, 0x07, 0x00, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x0A, 0x1A, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x0C, 0x00, 0xB8,
-0xFC, 0xF3, 0x3B, 0x23, 0x0D, 0x02, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x65, 0x08, 0x00,
-0xFC, 0xF3, 0x3B, 0x22, 0xFB, 0x00, 0x00
+ 0xFC, 0xF3, 0x3B, 0x22, 0xC0, 0x00, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xC1, 0x00, 0x01,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xC2, 0x00, 0x02,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xC3, 0x00, 0x02,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xC6, 0x00, 0x7D,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xC7, 0x00, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xC8, 0x00, 0x18,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xD2, 0x82, 0x94,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xEE, 0x00, 0x01,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xF5, 0x00, 0x03,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xF6, 0x00, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xF8, 0x80, 0x01,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xF9, 0x08, 0x7F,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xFA, 0x24, 0x8B,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x07, 0x00, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x0A, 0x1A, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x0C, 0x00, 0xB8,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x0D, 0x02, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x65, 0x08, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xFB, 0x00, 0x00
};
unsigned char HS_FACTORY_RCV_cmd[] = {
/*0xC0,*/
-0xFC, 0xF3, 0x3B, 0x22, 0xC0, 0x00, 0x00,
-0xFC, 0xF3, 0x3B, 0x22, 0xC1, 0x00, 0x00,
-0xFC, 0xF3, 0x3B, 0x22, 0xC2, 0x00, 0x02,
-0xFC, 0xF3, 0x3B, 0x22, 0xC3, 0x00, 0x02,
-0xFC, 0xF3, 0x3B, 0x22, 0xC6, 0x00, 0x7D,
-0xFC, 0xF3, 0x3B, 0x22, 0xC7, 0x00, 0x00,
-0xFC, 0xF3, 0x3B, 0x22, 0xC8, 0x00, 0x18,
-0xFC, 0xF3, 0x3B, 0x22, 0xD2, 0x82, 0x94,
-0xFC, 0xF3, 0x3B, 0x22, 0xEE, 0x00, 0x01,
-0xFC, 0xF3, 0x3B, 0x22, 0xF5, 0x00, 0x03,
-0xFC, 0xF3, 0x3B, 0x22, 0xF6, 0x00, 0x00,
-0xFC, 0xF3, 0x3B, 0x22, 0xF8, 0x80, 0x01,
-0xFC, 0xF3, 0x3B, 0x22, 0xF9, 0x08, 0x7F,
-0xFC, 0xF3, 0x3B, 0x22, 0xFA, 0x24, 0x8B,
-0xFC, 0xF3, 0x3B, 0x23, 0x07, 0x00, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x0A, 0x1A, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x0C, 0x00, 0xB8,
-0xFC, 0xF3, 0x3B, 0x23, 0x0D, 0x02, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x65, 0x08, 0x00,
-0xFC, 0xF3, 0x3B, 0x22, 0xFB, 0x00, 0x00
+ 0xFC, 0xF3, 0x3B, 0x22, 0xC0, 0x00, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xC1, 0x00, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xC2, 0x00, 0x02,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xC3, 0x00, 0x02,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xC6, 0x00, 0x7D,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xC7, 0x00, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xC8, 0x00, 0x18,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xD2, 0x82, 0x94,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xEE, 0x00, 0x01,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xF5, 0x00, 0x03,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xF6, 0x00, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xF8, 0x80, 0x01,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xF9, 0x08, 0x7F,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xFA, 0x24, 0x8B,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x07, 0x00, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x0A, 0x1A, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x0C, 0x00, 0xB8,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x0D, 0x02, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x65, 0x08, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xFB, 0x00, 0x00
};
unsigned char HS_FACTORY_SPK_cmd[] = {
/*0xC0,*/
-0xFC, 0xF3, 0x3B, 0x22, 0xC0, 0x00, 0x01,
-0xFC, 0xF3, 0x3B, 0x22, 0xC1, 0x00, 0x01,
-0xFC, 0xF3, 0x3B, 0x22, 0xC2, 0x00, 0x02,
-0xFC, 0xF3, 0x3B, 0x22, 0xC3, 0x00, 0x02,
-0xFC, 0xF3, 0x3B, 0x22, 0xC6, 0x00, 0x7D,
-0xFC, 0xF3, 0x3B, 0x22, 0xC7, 0x00, 0x00,
-0xFC, 0xF3, 0x3B, 0x22, 0xC8, 0x00, 0x18,
-0xFC, 0xF3, 0x3B, 0x22, 0xD2, 0x82, 0x94,
-0xFC, 0xF3, 0x3B, 0x22, 0xEE, 0x00, 0x01,
-0xFC, 0xF3, 0x3B, 0x22, 0xF5, 0x00, 0x03,
-0xFC, 0xF3, 0x3B, 0x22, 0xF6, 0x00, 0x00,
-0xFC, 0xF3, 0x3B, 0x22, 0xF8, 0x80, 0x01,
-0xFC, 0xF3, 0x3B, 0x22, 0xF9, 0x08, 0x7F,
-0xFC, 0xF3, 0x3B, 0x22, 0xFA, 0x24, 0x8B,
-0xFC, 0xF3, 0x3B, 0x23, 0x07, 0x00, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x0A, 0x1A, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x0C, 0x00, 0xB8,
-0xFC, 0xF3, 0x3B, 0x23, 0x0D, 0x02, 0x00,
-0xFC, 0xF3, 0x3B, 0x23, 0x65, 0x08, 0x00,
-0xFC, 0xF3, 0x3B, 0x22, 0xFB, 0x00, 0x00
+ 0xFC, 0xF3, 0x3B, 0x22, 0xC0, 0x00, 0x01,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xC1, 0x00, 0x01,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xC2, 0x00, 0x02,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xC3, 0x00, 0x02,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xC6, 0x00, 0x7D,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xC7, 0x00, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xC8, 0x00, 0x18,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xD2, 0x82, 0x94,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xEE, 0x00, 0x01,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xF5, 0x00, 0x03,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xF6, 0x00, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xF8, 0x80, 0x01,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xF9, 0x08, 0x7F,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xFA, 0x24, 0x8B,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x07, 0x00, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x0A, 0x1A, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x0C, 0x00, 0xB8,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x0D, 0x02, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x65, 0x08, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xFB, 0x00, 0x00
};
#else
unsigned char bypass_cmd[] = {
/*0xC0,*/
-0xFC, 0xF3, 0x3B, 0x22, 0xF5, 0x00, 0x03,
-0xFC, 0xF3, 0x3B, 0x22, 0xF8, 0x80, 0x03,
-0xFC, 0xF3, 0x3B, 0x22, 0xC6, 0x00, 0x7D,
-0xFC, 0xF3, 0x3B, 0x22, 0xC7, 0x00, 0x00,
-0xFC, 0xF3, 0x3B, 0x22, 0xC8, 0x00, 0x18,
-0xFC, 0xF3, 0x3B, 0x23, 0x0A, 0x1A, 0x00,
-0xFC, 0xF3, 0x3B, 0x22, 0xFA, 0x24, 0x8B,
-0xFC, 0xF3, 0x3B, 0x22, 0xF9, 0x00, 0x7F,
-0xFC, 0xF3, 0x3B, 0x22, 0xF6, 0x00, 0x00,
-0xFC, 0xF3, 0x3B, 0x22, 0xD2, 0x82, 0x94,
-0xFC, 0xF3, 0x3B, 0x22, 0xEE, 0x00, 0x01,
-0xFC, 0xF3, 0x3B, 0x22, 0xFB, 0x00, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xF5, 0x00, 0x03,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xF8, 0x80, 0x03,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xC6, 0x00, 0x7D,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xC7, 0x00, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xC8, 0x00, 0x18,
+ 0xFC, 0xF3, 0x3B, 0x23, 0x0A, 0x1A, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xFA, 0x24, 0x8B,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xF9, 0x00, 0x7F,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xF6, 0x00, 0x00,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xD2, 0x82, 0x94,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xEE, 0x00, 0x01,
+ 0xFC, 0xF3, 0x3B, 0x22, 0xFB, 0x00, 0x00,
};
#endif
@@ -864,7 +875,7 @@ static int fm34_i2c_write(char *txData, int length)
return 0;
}
-#if defined(CONFIG_MACH_C1_KOR_LGT) || defined(CONFIG_MACH_C1VZW)
+#if defined(CONFIG_MACH_C1_KOR_LGT)
void fm34_parameter_reset(void)
{
pr_info(MODULE_NAME "%s\n", __func__);
diff --git a/drivers/misc/max77693-muic.c b/drivers/misc/max77693-muic.c
index a902597..7dced6f 100644
--- a/drivers/misc/max77693-muic.c
+++ b/drivers/misc/max77693-muic.c
@@ -20,6 +20,7 @@
*/
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/reboot.h>
#include <linux/i2c.h>
#include <linux/gpio.h>
#include <plat/gpio-cfg.h>
@@ -32,7 +33,9 @@
#include <linux/mfd/max77693.h>
#include <linux/mfd/max77693-private.h>
#include <linux/host_notify.h>
+#include <linux/power_supply.h>
#include <plat/udc-hs.h>
+#include <linux/wakelock.h>
#ifdef CONFIG_USBHUB_USB3803
#include <linux/usb3803.h>
#endif
@@ -78,7 +81,11 @@ enum {
enum {
ADC_GND = 0x00,
+#if defined(CONFIG_MUIC_DET_JACK)
+ ADC_MHL_OR_SENDEND = 0x01,
+#else
ADC_MHL = 0x01,
+#endif
ADC_DOCK_PREV_KEY = 0x04,
ADC_DOCK_NEXT_KEY = 0x07,
ADC_DOCK_VOL_DN = 0x0a, /* 0x01010 14.46K ohm */
@@ -93,6 +100,9 @@ enum {
ADC_JIG_UART_OFF = 0x1c, /* 0x11100 523K ohm */
ADC_JIG_UART_ON = 0x1d, /* 0x11101 619K ohm */
ADC_CARDOCK = 0x1d, /* 0x11101 619K ohm */
+#if defined(CONFIG_MUIC_DET_JACK)
+ ADC_EARJACK = 0x1e, /* 0x11110 1000 or 1002 ohm */
+#endif
ADC_OPEN = 0x1f
};
@@ -122,6 +132,8 @@ struct max77693_muic_info {
int mansw;
bool is_default_uart_path_cp;
+ struct wake_lock muic_wake_lock;
+
enum cable_type_muic cable_type;
struct delayed_work init_work;
struct delayed_work usb_work;
@@ -134,6 +146,15 @@ struct max77693_muic_info {
struct input_dev *input;
int previous_key;
bool is_adc_open_prev;
+
+#if defined(CONFIG_MACH_GC1)
+ bool is_otg_attach_blocked;
+#endif /* CONFIG_MACH_GC1 */
+
+#if defined(CONFIG_MUIC_DET_JACK)
+ int earkeypressed;
+ int previous_earkey;
+#endif
#ifdef CONFIG_EXTCON
struct extcon_dev *edev;
#endif
@@ -184,7 +205,7 @@ static int max77693_muic_get_comp2_comn1_pass2
static int max77693_muic_set_comp2_comn1_pass2
(struct max77693_muic_info *info, int type, int path)
{
- /* type 1 == usb, type 2 == uart */
+ /* type 0 == usb, type 1 == uart */
u8 cntl1_val, cntl1_msk;
int ret = 0;
int val;
@@ -205,13 +226,13 @@ static int max77693_muic_set_comp2_comn1_pass2
}
} else if (type == 1) {
if (path == UART_PATH_AP) {
- info->muic_data->sw_path = UART_PATH_AP;
+ info->muic_data->uart_path = UART_PATH_AP;
if (info->is_default_uart_path_cp)
val = MAX77693_MUIC_CTRL1_BIN_5_101;
else
val = MAX77693_MUIC_CTRL1_BIN_3_011;
} else if (path == UART_PATH_CP) {
- info->muic_data->sw_path = UART_PATH_CP;
+ info->muic_data->uart_path = UART_PATH_CP;
if (info->is_default_uart_path_cp)
val = MAX77693_MUIC_CTRL1_BIN_3_011;
else
@@ -239,7 +260,7 @@ static int max77693_muic_set_comp2_comn1_pass2
}
#ifdef CONFIG_LTE_VIA_SWITCH
else if (path == UART_PATH_LTE) {
- info->muic_data->sw_path = UART_PATH_LTE;
+ info->muic_data->uart_path = UART_PATH_LTE;
val = MAX77693_MUIC_CTRL1_BIN_5_101;
if (gpio_is_valid(GPIO_LTE_VIA_UART_SEL)) {
gpio_set_value(GPIO_LTE_VIA_UART_SEL,
@@ -258,7 +279,13 @@ static int max77693_muic_set_comp2_comn1_pass2
, __func__);
return -EINVAL;
}
- } else {
+ }
+#if defined(CONFIG_MUIC_DET_JACK)
+ else if (type == 2) {
+ val = MAX77693_MUIC_CTRL1_BIN_2_010;
+ }
+#endif
+ else {
dev_err(info->dev, "func: %s invalid path type(%d)\n"
, __func__, type);
return -EINVAL;
@@ -267,8 +294,11 @@ static int max77693_muic_set_comp2_comn1_pass2
cntl1_val = (val << COMN1SW_SHIFT) | (val << COMP2SW_SHIFT);
cntl1_msk = COMN1SW_MASK | COMP2SW_MASK;
- max77693_update_reg(info->muic, MAX77693_MUIC_REG_CTRL1, cntl1_val,
- cntl1_msk);
+ ret = max77693_update_reg(info->muic, MAX77693_MUIC_REG_CTRL1,
+ cntl1_val, cntl1_msk);
+
+ dev_info(info->dev, "%s: CNTL1(0x%02x) ret: %d\n",
+ __func__, cntl1_val, ret);
return ret;
}
@@ -345,6 +375,18 @@ static int max77693_muic_get_uart_path_pass2
}
}
+#if defined(CONFIG_MUIC_DET_JACK)
+static int max77693_muic_set_audio_path_pass2
+ (struct max77693_muic_info *info, int path)
+{
+ int ret = 0;
+ ret = max77693_muic_set_comp2_comn1_pass2
+ (info, 2/*audio*/, path);
+ return ret;
+
+}
+#endif
+
static ssize_t max77693_muic_show_usb_state(struct device *dev,
struct device_attribute *attr,
char *buf)
@@ -412,9 +454,12 @@ static ssize_t max77693_muic_show_manualsw(struct device *dev,
{
struct max77693_muic_info *info = dev_get_drvdata(dev);
+#if !defined(CONFIG_MACH_T0) && !defined(CONFIG_MACH_M3) \
+ && !defined(CONFIG_MACH_SLP_T0_LTE)
dev_info(info->dev, "func:%s ap(0),cp(1),vps(2)sw_path:%d(%d)\n",
__func__, info->muic_data->sw_path,
gpio_get_value(GPIO_USB_SEL));/*For debuging*/
+#endif
switch (info->muic_data->sw_path) {
case AP_USB_MODE:
@@ -423,6 +468,10 @@ static ssize_t max77693_muic_show_manualsw(struct device *dev,
return sprintf(buf, "MODEM\n");
case AUDIO_MODE:
return sprintf(buf, "Audio\n");
+#if defined(CONFIG_SWITCH_DUAL_MODEM)
+ case CP_ESC_USB_MODE:
+ return sprintf(buf, "ESC\n");
+#endif
default:
break;
}
@@ -443,7 +492,19 @@ static ssize_t max77693_muic_set_manualsw(struct device *dev,
dev_info(info->dev, "%s: AP_USB_MODE\n", __func__);
} else if (!strncasecmp(buf, "MODEM", 5)) {
info->muic_data->sw_path = CP_USB_MODE;
+#if defined(CONFIG_SWITCH_DUAL_MODEM)
+ gpio_set_value(GPIO_USB_SEL, GPIO_LEVEL_LOW);
+ dev_info(info->dev, "%s: MODEM %d\n", __func__,
+ gpio_get_value(GPIO_USB_SEL));
+#endif
dev_info(info->dev, "%s: CP_USB_MODE\n", __func__);
+#if defined(CONFIG_SWITCH_DUAL_MODEM)
+ } else if (!strncasecmp(buf, "ESC", 3)) {
+ info->muic_data->sw_path = CP_ESC_USB_MODE;
+ gpio_set_value(GPIO_USB_SEL, GPIO_LEVEL_HIGH);
+ dev_info(info->dev, "%s: ESC %d\n", __func__,
+ gpio_get_value(GPIO_USB_SEL));
+#endif
} else
dev_warn(info->dev, "%s: Wrong command\n", __func__);
@@ -636,6 +697,8 @@ static ssize_t max77693_muic_set_uart_sel(struct device *dev,
struct max77693_muic_info *info = dev_get_drvdata(dev);
if (info->max77693->pmic_rev < MAX77693_REV_PASS2) {
+#if !defined(CONFIG_MACH_T0) && !defined(CONFIG_MACH_M3) \
+ && !defined(CONFIG_MACH_SLP_T0_LTE)
if (!strncasecmp(buf, "AP", 2)) {
info->muic_data->uart_path = UART_PATH_AP;
if (gpio_is_valid(GPIO_UART_SEL)) {
@@ -683,6 +746,7 @@ static ssize_t max77693_muic_set_uart_sel(struct device *dev,
#endif
else
dev_warn(info->dev, "%s: Wrong command\n", __func__);
+#endif /* !defined(CONFIG_MACH_T0) */
} else if (info->max77693->pmic_rev >= MAX77693_REV_PASS2) {
if (!strncasecmp(buf, "AP", 2)) {
int ret = max77693_muic_set_uart_path_pass2
@@ -700,6 +764,19 @@ static ssize_t max77693_muic_set_uart_sel(struct device *dev,
else
dev_err(info->dev, "%s: Change(CP) fail!!"
, __func__);
+#if defined(CONFIG_SWITCH_DUAL_MODEM)
+ if (gpio_is_valid(GPIO_UART_SEL)) {
+ gpio_set_value(GPIO_UART_SEL, GPIO_LEVEL_LOW);
+ dev_info(info->dev,
+ "%s: CP %d\n",
+ __func__,
+ gpio_get_value(GPIO_UART_SEL));
+ } else {
+ dev_err(info->dev,
+ "%s: Change GPIO failed",
+ __func__);
+ }
+#endif
}
#ifdef CONFIG_LTE_VIA_SWITCH
else if (!strncasecmp(buf, "LTE", 3)) {
@@ -712,6 +789,29 @@ static ssize_t max77693_muic_set_uart_sel(struct device *dev,
, __func__);
}
#endif
+#if defined(CONFIG_SWITCH_DUAL_MODEM)
+ else if (!strncasecmp(buf, "ESC", 3)) {
+ int ret = max77693_muic_set_uart_path_pass2
+ (info, UART_PATH_CP);
+ if (ret >= 0)
+ info->muic_data->uart_path = UART_PATH_CP;
+ else
+ dev_err(info->dev, "%s: Change(CP_ESC) fail!!"
+ , __func__);
+ if (gpio_is_valid(GPIO_UART_SEL)) {
+ gpio_set_value(GPIO_UART_SEL, GPIO_LEVEL_HIGH);
+ info->muic_data->uart_path = UART_PATH_CP_ESC;
+ dev_info(info->dev, "%s: ESC %d\n",
+ __func__,
+ gpio_get_value(GPIO_UART_SEL));
+ } else {
+ dev_err(info->dev,
+ "%s: Change GPIO failed",
+ __func__);
+ }
+ }
+#endif
+
else {
dev_warn(info->dev, "%s: Wrong command\n"
, __func__);
@@ -726,6 +826,8 @@ static ssize_t max77693_muic_show_uart_sel(struct device *dev,
{
struct max77693_muic_info *info = dev_get_drvdata(dev);
if (info->max77693->pmic_rev < MAX77693_REV_PASS2) {
+#if !defined(CONFIG_MACH_T0) && !defined(CONFIG_MACH_M3) \
+ && !defined(CONFIG_MACH_SLP_T0_LTE)
switch (info->muic_data->uart_path) {
case UART_PATH_AP:
if (gpio_get_value(GPIO_UART_SEL) == GPIO_LEVEL_HIGH)
@@ -759,27 +861,23 @@ static ssize_t max77693_muic_show_uart_sel(struct device *dev,
default:
break;
}
+#endif /* !defined(CONFIG_MACH_T0) */
} else if (info->max77693->pmic_rev >= MAX77693_REV_PASS2) {
- int val = max77693_muic_get_uart_path_pass2(info);
switch (info->muic_data->uart_path) {
case UART_PATH_AP:
- if (val == UART_PATH_AP)
- return sprintf(buf, "AP\n");
- else
- return sprintf(buf, "ERR_AP\n");
+ return sprintf(buf, "AP\n");
break;
case UART_PATH_CP:
- if (val == UART_PATH_CP)
- return sprintf(buf, "CP\n");
- else
- return sprintf(buf, "ERR_CP\n");
+ return sprintf(buf, "CP\n");
break;
#ifdef CONFIG_LTE_VIA_SWITCH
case UART_PATH_LTE:
- if (val == UART_PATH_LTE)
- return sprintf(buf, "LTE\n");
- else
- return sprintf(buf, "ERR_LTE\n");
+ return sprintf(buf, "LTE\n");
+ break;
+#endif
+#if defined(CONFIG_SWITCH_DUAL_MODEM)
+ case UART_PATH_CP_ESC:
+ return sprintf(buf, "ESC\n");
break;
#endif
default:
@@ -789,6 +887,53 @@ static ssize_t max77693_muic_show_uart_sel(struct device *dev,
return sprintf(buf, "UNKNOWN\n");
}
+#if defined(CONFIG_MACH_GC1)
+static ssize_t max77693_muic_show_otg_block(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct max77693_muic_info *info = dev_get_drvdata(dev);
+ const char *mode;
+
+ /* "BLOCK": blocked(true), "ALLOW": allowed(false) */
+ if (info->is_otg_attach_blocked) {
+ pr_info("%s:%s otg attach=blocked\n", DEV_NAME, __func__);
+ mode = "BLOCK";
+ } else {
+ pr_info("%s:%s otg attach=allowed\n", DEV_NAME, __func__);
+ mode = "ALLOW";
+ }
+
+ return sprintf(buf, "%s\n", mode);
+}
+
+static ssize_t max77693_muic_set_otg_block(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct max77693_muic_info *info = dev_get_drvdata(dev);
+ const char *mode;
+
+ pr_info("%s:%s buf:%s\n", DEV_NAME, __func__, buf);
+
+ /* "BLOCK": blocked(true), "ALLOW": allowed(false) */
+ if (!strncmp(buf, "BLOCK", 5)) {
+ info->is_otg_attach_blocked = true;
+ mode = "BLOCK";
+ } else if (!strncmp(buf, "ALLOW", 5)) {
+ info->is_otg_attach_blocked = false;
+ mode = "ALLOW";
+ } else {
+ dev_warn(info->dev, "%s: Wrong command\n", __func__);
+ return count;
+ }
+
+ pr_info("%s:%s otg attach=%s\n", DEV_NAME, __func__, mode);
+
+ return count;
+}
+#endif /* CONFIG_MACH_GC1 */
+
#ifdef CONFIG_LTE_VIA_SWITCH
static ssize_t max77693_muic_show_check_cpboot(struct device *dev,
struct device_attribute *attr, char *buf)
@@ -852,6 +997,11 @@ static DEVICE_ATTR(otg_test, 0664,
static DEVICE_ATTR(adc_debounce_time, 0664,
max77693_muic_show_adc_debounce_time,
max77693_muic_set_adc_debounce_time);
+#if defined(CONFIG_MACH_GC1)
+static DEVICE_ATTR(otg_block, 0664,
+ max77693_muic_show_otg_block,
+ max77693_muic_set_otg_block);
+#endif /* CONFIG_MACH_GC1 */
#ifdef CONFIG_LTE_VIA_SWITCH
static DEVICE_ATTR(check_cpboot, 0664,
max77693_muic_show_check_cpboot,
@@ -867,6 +1017,9 @@ static struct attribute *max77693_muic_attributes[] = {
&dev_attr_audio_path.attr,
&dev_attr_otg_test.attr,
&dev_attr_adc_debounce_time.attr,
+#if defined(CONFIG_MACH_GC1)
+ &dev_attr_otg_block.attr,
+#endif /* CONFIG_MACH_GC1 */
#ifdef CONFIG_LTE_VIA_SWITCH
&dev_attr_check_cpboot.attr,
#endif
@@ -877,7 +1030,7 @@ static const struct attribute_group max77693_muic_group = {
.attrs = max77693_muic_attributes,
};
-#if defined(CONFIG_MACH_M0_CTC)
+#if defined(CONFIG_MACH_M0_CTC) || defined(CONFIG_MACH_T0_CHN_CTC)
/*0 : usb is not conneted to CP 1 : usb is connected to CP */
int cp_usb_state;
@@ -947,7 +1100,8 @@ static int max77693_muic_set_usb_path(struct max77693_muic_info *info, int path)
&& info->cable_type != CABLE_TYPE_DESKDOCK_MUIC)
#endif
max77693_update_reg(client, MAX77693_MUIC_REG_CTRL1, cntl1_val,
- cntl1_msk);
+ cntl1_msk);
+
max77693_update_reg(client, MAX77693_MUIC_REG_CTRL2,
CTRL2_CPEn1_LOWPWD0,
CTRL2_CPEn_MASK | CTRL2_LOWPWD_MASK);
@@ -1175,10 +1329,14 @@ static int max77693_muic_attach_usb_type(struct max77693_muic_info *info,
info->cable_type = CABLE_TYPE_NONE_MUIC;
return ret;
}
-
+#if defined(CONFIG_SWITCH_DUAL_MODEM)
+ if (mdata->sw_path == CP_USB_MODE ||
+ mdata->sw_path == CP_ESC_USB_MODE) {
+#else
if (mdata->sw_path == CP_USB_MODE) {
+#endif
info->cable_type = CABLE_TYPE_USB_MUIC;
-#if defined(CONFIG_MACH_M0_CTC)
+#if defined(CONFIG_MACH_M0_CTC) || defined(CONFIG_MACH_T0_CHN_CTC)
if (system_rev < 11) {
gpio_direction_output(GPIO_USB_BOOT_EN, 1);
} else if (system_rev == 11) {
@@ -1189,6 +1347,16 @@ static int max77693_muic_attach_usb_type(struct max77693_muic_info *info,
}
cp_usb_state = 1;
#endif
+#if defined(CONFIG_SWITCH_DUAL_MODEM)
+ if (mdata->sw_path == CP_USB_MODE) {
+ gpio_set_value(GPIO_USB_SEL, GPIO_LEVEL_LOW);
+#if defined(CONFIG_MACH_T0_CHN_CMCC)
+ gpio_direction_output(GPIO_CP_USB_ON, 1);
+#endif
+ } else {
+ gpio_set_value(GPIO_USB_SEL, GPIO_LEVEL_HIGH);
+ }
+#endif
max77693_muic_set_usb_path(info, CP_USB_MODE);
return 0;
}
@@ -1230,8 +1398,8 @@ static int max77693_muic_attach_dock_type(struct max77693_muic_info *info,
info->cable_type = CABLE_TYPE_DESKDOCK_MUIC;
path = AUDIO_MODE;
- if (mdata->deskdock_cb)
- mdata->deskdock_cb(MAX77693_MUIC_ATTACHED);
+ if (mdata->dock_cb)
+ mdata->dock_cb(MAX77693_MUIC_DOCK_DESKDOCK);
break;
case ADC_CARDOCK:
/* Car Dock */
@@ -1244,8 +1412,29 @@ static int max77693_muic_attach_dock_type(struct max77693_muic_info *info,
info->cable_type = CABLE_TYPE_CARDOCK_MUIC;
path = AUDIO_MODE;
- if (mdata->cardock_cb)
- mdata->cardock_cb(MAX77693_MUIC_ATTACHED);
+ if (mdata->dock_cb)
+ mdata->dock_cb(MAX77693_MUIC_DOCK_CARDOCK);
+ break;
+ case ADC_SMARTDOCK:
+ if (info->cable_type == CABLE_TYPE_SMARTDOCK_MUIC) {
+ dev_info(info->dev, "%s: duplicated(SmartDock)\n",
+ __func__);
+ return 0;
+ }
+ dev_info(info->dev, "%s:SmartDock\n", __func__);
+ info->cable_type = CABLE_TYPE_SMARTDOCK_MUIC;
+ path = AP_USB_MODE;
+ max77693_muic_set_charging_type(info, false);
+ msleep(40);
+#ifdef CONFIG_EXTCON
+ if (info->edev && info->is_mhl_ready)
+ extcon_set_cable_state(info->edev, "MHL", true);
+#else
+ if (mdata->mhl_cb && info->is_mhl_ready)
+ mdata->mhl_cb(MAX77693_MUIC_ATTACHED);
+#endif
+ if (mdata->dock_cb)
+ mdata->dock_cb(MAX77693_MUIC_DOCK_SMARTDOCK);
break;
default:
dev_info(info->dev, "%s: should not reach here(0x%x)\n",
@@ -1258,6 +1447,7 @@ static int max77693_muic_attach_dock_type(struct max77693_muic_info *info,
return 0;
}
+#if !defined(CONFIG_MUIC_MAX77693_SEPARATE_MHL_PORT)
static void max77693_muic_attach_mhl(struct max77693_muic_info *info, u8 chgtyp)
{
struct max77693_muic_data *mdata = info->muic_data;
@@ -1291,6 +1481,65 @@ static void max77693_muic_attach_mhl(struct max77693_muic_info *info, u8 chgtyp)
max77693_muic_set_charging_type(info, false);
}
}
+#endif /* !CONFIG_MUIC_MAX77693_SEPARATE_MHL_PORT */
+
+#if defined(CONFIG_MUIC_DET_JACK)
+static int max77693_muic_attach_earjack(struct max77693_muic_info *info,
+ int adc)
+{
+ struct max77693_muic_data *mdata = info->muic_data;
+
+ if (info->cable_type == CABLE_TYPE_EARJACK_MUIC) {
+ dev_info(info->dev, "%s: duplicated(EarJack)\n",
+ __func__);
+ return 0;
+ }
+
+ dev_info(info->dev, "%s:EarJack\n", __func__);
+ info->cable_type = CABLE_TYPE_EARJACK_MUIC;
+
+ if (mdata->earjack_cb)
+ mdata->earjack_cb(MAX77693_MUIC_ATTACHED);
+
+ max77693_muic_set_audio_path_pass2(info, 0);
+
+ return 0;
+}
+
+static int max77693_muic_press_earjack_key(struct max77693_muic_info *info,
+ int adc)
+{
+ struct max77693_muic_data *mdata = info->muic_data;
+ unsigned int code;
+
+ if (info->earkeypressed) {
+ switch (adc) {
+ case ADC_MHL_OR_SENDEND:
+ code = KEY_MEDIA;
+ info->previous_earkey = KEY_MEDIA;
+ break;
+ case ADC_DOCK_VOL_UP:
+ code = KEY_VOLUMEUP;
+ info->previous_earkey = KEY_VOLUMEUP;
+ break;
+ case ADC_DOCK_VOL_DN:
+ code = KEY_VOLUMEDOWN;
+ info->previous_earkey = KEY_VOLUMEDOWN;
+ break;
+ default:
+ dev_info(info->dev, "%s: should not reach here(0x%x)\n",
+ __func__, adc);
+ return 0;
+ }
+ } else
+ code = info->previous_earkey;
+
+ if (mdata->earjackkey_cb)
+ mdata->earjackkey_cb(info->earkeypressed, code);
+
+ return 0;
+}
+#endif
static void max77693_muic_handle_jig_uart(struct max77693_muic_info *info,
u8 vbvolt)
@@ -1300,6 +1549,8 @@ static void max77693_muic_handle_jig_uart(struct max77693_muic_info *info,
bool is_otgtest = false;
u8 cntl1_val, cntl1_msk;
u8 val = MAX77693_MUIC_CTRL1_BIN_3_011;
+ int ret = 0;
+
dev_info(info->dev, "func:%s vbvolt:%x cable_type:%d\n",
__func__, vbvolt, info->cable_type);
dev_info(info->dev, "%s: JIG UART/BOOTOFF(0x%x)\n", __func__, vbvolt);
@@ -1334,6 +1585,17 @@ static void max77693_muic_handle_jig_uart(struct max77693_muic_info *info,
}
}
#endif
+#if defined(CONFIG_SWITCH_DUAL_MODEM)
+ if (gpio_is_valid(GPIO_UART_SEL)) {
+ gpio_set_value(GPIO_UART_SEL, GPIO_LEVEL_LOW);
+ dev_info(info->dev,
+ "%s: UART_SEL_GPIO_LEVEL_LOW",
+ __func__);
+ } else
+ dev_err(info->dev,
+ "%s: ERR_UART_SEL_GPIO_SET_HIGH\n"
+ , __func__);
+#endif
}
#ifdef CONFIG_LTE_VIA_SWITCH
else if (info->muic_data->uart_path == UART_PATH_LTE) {
@@ -1349,6 +1611,21 @@ static void max77693_muic_handle_jig_uart(struct max77693_muic_info *info,
, __func__);
}
#endif
+#if defined(CONFIG_SWITCH_DUAL_MODEM)
+ else if (info->muic_data->uart_path == UART_PATH_CP_ESC) {
+ val = MAX77693_MUIC_CTRL1_BIN_5_101;
+ if (gpio_is_valid(GPIO_UART_SEL)) {
+ gpio_set_value(GPIO_UART_SEL, GPIO_LEVEL_HIGH);
+ dev_info(info->dev,
+ "%s: UART_SEL_GPIO_LEVEL_HIGH",
+ __func__);
+ } else
+ dev_err(info->dev,
+ "%s: ERR_UART_SEL_GPIO_SET_LOW\n"
+ , __func__);
+ }
+#endif
+
else
val = MAX77693_MUIC_CTRL1_BIN_3_011;
} else
@@ -1356,8 +1633,11 @@ static void max77693_muic_handle_jig_uart(struct max77693_muic_info *info,
cntl1_val = (val << COMN1SW_SHIFT) | (val << COMP2SW_SHIFT);
cntl1_msk = COMN1SW_MASK | COMP2SW_MASK;
- max77693_update_reg(info->muic, MAX77693_MUIC_REG_CTRL1, cntl1_val,
- cntl1_msk);
+ ret = max77693_update_reg(info->muic, MAX77693_MUIC_REG_CTRL1,
+ cntl1_val, cntl1_msk);
+
+ dev_info(info->dev, "%s: CNTL1(0x%02x) ret: %d\n",
+ __func__, cntl1_val, ret);
max77693_update_reg(info->muic, MAX77693_MUIC_REG_CTRL2,
CTRL2_CPEn1_LOWPWD0,
@@ -1392,6 +1672,9 @@ static void max77693_muic_handle_jig_uart(struct max77693_muic_info *info,
void max77693_otg_control(struct max77693_muic_info *info, int enable)
{
u8 int_mask, cdetctrl1, chg_cnfg_00;
+#ifdef CONFIG_MACH_GC1
+ u8 mu_adc = max77693_muic_get_status1_adc_value();
+#endif
pr_info("%s: enable(%d)\n", __func__, enable);
if (enable) {
@@ -1401,6 +1684,26 @@ void max77693_otg_control(struct max77693_muic_info *info, int enable)
chg_int_state = int_mask;
int_mask |= (1 << 4); /* disable chgin intr */
int_mask |= (1 << 6); /* disable chg */
+
+#ifdef CONFIG_MACH_GC1
+ /* In Factory mode using anyway Jig to switch between
+ * USB <--> UART sees a momentary 301K resistance as that of an
+ * OTG. Disabling charging INTRS now can lead to USB and MTP
+ * drivers not getting recognized in subsequent switches.
+ * Factory Mode BOOT(on) USB.
+ */
+
+ /* Wait for the signal debounce time adjustment for 10 ms*/
+ mdelay(10);
+
+ if (mu_adc) {
+ pr_info("%s: JIG USB CABLE adc(0x%x))\n",
+ __func__, mu_adc);
+ pr_info(" %s: Enabling charging INT"\
+ "for the Non-OTG casey.\n", __func__);
+ int_mask &= ~(1 << 6); /* Enabling Chgin INTR.*/
+ }
+#endif
int_mask &= ~(1 << 0); /* enable byp intr */
max77693_write_reg(info->max77693->i2c,
MAX77693_CHG_REG_CHG_INT_MASK, int_mask);
@@ -1409,6 +1712,15 @@ void max77693_otg_control(struct max77693_muic_info *info, int enable)
max77693_read_reg(info->max77693->muic,
MAX77693_MUIC_REG_CDETCTRL1, &cdetctrl1);
cdetctrl1 &= ~(1 << 0);
+#ifdef CONFIG_MACH_GC1
+ /* Factory Mode BOOT(on) USB */
+ if (mu_adc) {
+ pr_info("%s: Enabling Charging Detn. for non-OTG\n",
+ __func__);
+ /*Enabling Charger Detn on Rising VB */
+ cdetctrl1 |= (1 << 0);
+ }
+#endif
max77693_write_reg(info->max77693->muic,
MAX77693_MUIC_REG_CDETCTRL1, cdetctrl1);
@@ -1490,11 +1802,157 @@ void powered_otg_control(int enable)
max77693_powered_otg_control(gInfo, enable);
}
+#if defined(CONFIG_MUIC_MAX77693_SUPPORT_OTG_AUDIO_DOCK)
+#define BAT_PSY_NAME "battery"
+void max77693_muic_attach_audio_dock(void)
+{
+ struct max77693_muic_info *info = gInfo;
+ struct max77693_muic_data *mdata = info->muic_data;
+ struct power_supply *psy = power_supply_get_by_name(BAT_PSY_NAME);
+ union power_supply_propval value;
+
+ switch (info->cable_type) {
+#if defined(CONFIG_MUIC_MAX77693_SUPPORT_SMART_DOCK)
+ case CABLE_TYPE_SMARTDOCK_MUIC:
+ case CABLE_TYPE_SMARTDOCK_TA_MUIC:
+ case CABLE_TYPE_SMARTDOCK_USB_MUIC:
+ pr_info("%s:%s= SmartDock connected, ignore.\n", __FILE__,
+ __func__);
+ return;
+#endif /* CONFIG_MUIC_MAX77693_SUPPORT_SMART_DOCK */
+ default:
+ break;
+ }
+
+ dev_info(info->dev, "%s:AudioDock\n", __func__);
+
+ info->cable_type = CABLE_TYPE_AUDIODOCK_MUIC;
+
+ max77693_otg_control(info, 0);
+
+ if (mdata->dock_cb)
+ mdata->dock_cb(MAX77693_MUIC_DOCK_AUDIODOCK);
+
+ if (!psy || !psy->set_property) {
+ pr_err("%s: fail to get %s psy\n", __func__, BAT_PSY_NAME);
+ return;
+ }
+
+ psy->set_property(psy, POWER_SUPPLY_PROP_STATUS, &value);
+}
+#endif /* CONFIG_MUIC_MAX77693_SUPPORT_OTG_AUDIO_DOCK */
+
+#if defined(CONFIG_MUIC_MAX77693_SUPPORT_SMART_DOCK)
+static void max77693_muic_detach_smart_dock(struct max77693_muic_info *info)
+{
+ struct max77693_muic_data *mdata = info->muic_data;
+
+ pr_info("%s:%s\n", DEV_NAME, __func__);
+
+ switch (info->cable_type) {
+ case CABLE_TYPE_SMARTDOCK_TA_MUIC:
+ pr_info("%s:%s SMARTDOCK+TA\n", DEV_NAME, __func__);
+
+ if (mdata->usb_cb && info->is_usb_ready)
+ mdata->usb_cb(USB_POWERED_HOST_DETACHED);
+ break;
+ case CABLE_TYPE_SMARTDOCK_USB_MUIC:
+ pr_info("%s:%s SMARTDOCK+USB\n", DEV_NAME, __func__);
+
+ if (mdata->usb_cb && info->is_usb_ready)
+ mdata->usb_cb(USB_CABLE_DETACHED);
+ break;
+ case CABLE_TYPE_SMARTDOCK_MUIC:
+ pr_info("%s:%s SMARTDOCK\n", DEV_NAME, __func__);
+ break;
+ default:
+ pr_info("%s:%s cable_type is not SMARTDOCK\n", DEV_NAME,
+ __func__);
+ return;
+ break;
+ }
+
+ info->cable_type = CABLE_TYPE_NONE_MUIC;
+
+ if (mdata->dock_cb) {
+ mdata->dock_cb(MAX77693_MUIC_DOCK_DETACHED);
+ msleep(20);
+ }
+ max77693_muic_set_charging_type(info, false);
+#ifdef CONFIG_EXTCON
+ if (info->edev && info->is_mhl_ready)
+ extcon_set_cable_state(info->edev, "MHL", false);
+#else
+ if (mdata->mhl_cb && info->is_mhl_ready)
+ mdata->mhl_cb(MAX77693_MUIC_DETACHED);
+#endif
+}
+
+static void max77693_muic_attach_smart_dock(struct max77693_muic_info *info,
+ u8 adc, u8 vbvolt, u8 chgtyp)
+{
+ struct max77693_muic_data *mdata = info->muic_data;
+
+ switch (info->cable_type) {
+ case CABLE_TYPE_SMARTDOCK_MUIC:
+ if (chgtyp == CHGTYP_DEDICATED_CHGR) {
+ pr_info("%s:%s SMART_DOCK+TA=OTG Enable\n", DEV_NAME,
+ __func__);
+
+ if (mdata->usb_cb && info->is_usb_ready)
+ mdata->usb_cb(USB_POWERED_HOST_ATTACHED);
+
+ info->cable_type = CABLE_TYPE_SMARTDOCK_TA_MUIC;
+ } else if (chgtyp == CHGTYP_USB) {
+ pr_info("%s:%s SMART_DOCK+USB=USB Enable\n", DEV_NAME,
+ __func__);
+
+ if (mdata->usb_cb && info->is_usb_ready)
+ mdata->usb_cb(USB_CABLE_ATTACHED);
+
+ info->cable_type = CABLE_TYPE_SMARTDOCK_USB_MUIC;
+ } else
+ pr_info("%s:%s SMART_DOCK + [%d] = ?\n", DEV_NAME,
+ __func__, chgtyp);
+ break;
+ case CABLE_TYPE_SMARTDOCK_TA_MUIC:
+ if (chgtyp == CHGTYP_DEDICATED_CHGR)
+ pr_info("%s:%s Duplicated(SMARTDOCK+TA)\n", DEV_NAME,
+ __func__);
+ else if (vbvolt)
+ pr_info("%s:%s SMART_DOCK + TA -> chgtyp:%x\n",
+ DEV_NAME, __func__, chgtyp);
+ else
+ max77693_muic_detach_smart_dock(info);
+ break;
+ case CABLE_TYPE_SMARTDOCK_USB_MUIC:
+ if (chgtyp == CHGTYP_USB)
+ pr_info("%s:%s Duplicated(SMARTDOCK+USB)\n", DEV_NAME,
+ __func__);
+ else if (vbvolt)
+ pr_info("%s:%s SMART_DOCK + USB -> chgtyp:%x\n",
+ DEV_NAME, __func__, chgtyp);
+ else
+ max77693_muic_detach_smart_dock(info);
+ break;
+ default:
+ if (vbvolt) {
+ pr_info("%s:%s SMART_DOCK+vbvolt=chgdetrun\n",
+ DEV_NAME, __func__);
+ max77693_muic_attach_dock_type(info, adc);
+ } else {
+ dev_warn(info->dev, "no vbus in SAMRTDOCK\n");
+ }
+ break;
+ }
+}
+#endif /* CONFIG_MUIC_MAX77693_SUPPORT_SMART_DOCK */
+
static int max77693_muic_handle_attach(struct max77693_muic_info *info,
u8 status1, u8 status2, int irq)
{
struct max77693_muic_data *mdata = info->muic_data;
- u8 adc, vbvolt, chgtyp, chgdetrun, adc1k;
+ u8 adc, vbvolt, chgtyp, chgdetrun, adc1k, dxovp;
int ret = 0;
adc = status1 & STATUS1_ADC_MASK;
@@ -1502,17 +1960,20 @@ static int max77693_muic_handle_attach(struct max77693_muic_info *info,
chgtyp = status2 & STATUS2_CHGTYP_MASK;
vbvolt = status2 & STATUS2_VBVOLT_MASK;
chgdetrun = status2 & STATUS2_CHGDETRUN_MASK;
+ dxovp = status2 & STATUS2_DXOVP_MASK;
dev_info(info->dev, "func:%s st1:%x st2:%x cable_type:%d\n",
__func__, status1, status2, info->cable_type);
- /* Workaround for Factory mode.
- * Abandon adc interrupt of approximately +-100K range
- * if previous cable status was JIG UART BOOT OFF.
- */
- if (info->cable_type == CABLE_TYPE_JIG_UART_OFF_MUIC ||
- info->cable_type == CABLE_TYPE_JIG_UART_OFF_VB_MUIC) {
+
+ switch (info->cable_type) {
+ case CABLE_TYPE_JIG_UART_OFF_MUIC:
+ case CABLE_TYPE_JIG_UART_OFF_VB_MUIC:
+ /* Workaround for Factory mode.
+ * Abandon adc interrupt of approximately +-100K range
+ * if previous cable status was JIG UART BOOT OFF.
+ */
if (adc == (ADC_JIG_UART_OFF + 1) ||
- adc == (ADC_JIG_UART_OFF - 1)) {
+ adc == (ADC_JIG_UART_OFF - 1)) {
/* Workaround for factory mode in MUIC PASS2
* In uart path cp, adc is unstable state
* MUIC PASS2 turn to AP_UART mode automatically
@@ -1522,29 +1983,74 @@ static int max77693_muic_handle_attach(struct max77693_muic_info *info,
if (info->muic_data->uart_path == UART_PATH_CP
&& info->max77693->pmic_rev >= MAX77693_REV_PASS2)
max77693_muic_handle_jig_uart(info, vbvolt);
+
dev_warn(info->dev, "%s: abandon ADC\n", __func__);
return 0;
}
- }
- if (info->cable_type == CABLE_TYPE_DESKDOCK_MUIC
- && adc != ADC_DESKDOCK) {
- dev_warn(info->dev, "%s: assume deskdock detach\n", __func__);
- info->cable_type = CABLE_TYPE_NONE_MUIC;
+ if (adc != ADC_JIG_UART_OFF) {
+ dev_warn(info->dev, "%s: assume jig uart off detach\n",
+ __func__);
+ info->cable_type = CABLE_TYPE_NONE_MUIC;
+ }
+ break;
- max77693_muic_set_charging_type(info, false);
- info->is_adc_open_prev = false;
- if (mdata->deskdock_cb)
- mdata->deskdock_cb(MAX77693_MUIC_DETACHED);
- } else if (info->cable_type == CABLE_TYPE_CARDOCK_MUIC
- && adc != ADC_CARDOCK) {
- dev_warn(info->dev, "%s: assume cardock detach\n", __func__);
- info->cable_type = CABLE_TYPE_NONE_MUIC;
+ case CABLE_TYPE_DESKDOCK_MUIC:
+ if (adc != ADC_DESKDOCK) {
+ dev_warn(info->dev, "%s: assume deskdock detach\n",
+ __func__);
+ info->cable_type = CABLE_TYPE_NONE_MUIC;
- max77693_muic_set_charging_type(info, false);
- info->is_adc_open_prev = false;
- if (mdata->cardock_cb)
- mdata->cardock_cb(MAX77693_MUIC_DETACHED);
+ max77693_muic_set_charging_type(info, false);
+ info->is_adc_open_prev = false;
+ if (mdata->dock_cb)
+ mdata->dock_cb(MAX77693_MUIC_DOCK_DETACHED);
+ }
+ break;
+
+ case CABLE_TYPE_CARDOCK_MUIC:
+ if (adc != ADC_CARDOCK) {
+ dev_warn(info->dev, "%s: assume cardock detach\n",
+ __func__);
+ info->cable_type = CABLE_TYPE_NONE_MUIC;
+
+ max77693_muic_set_charging_type(info, false);
+ info->is_adc_open_prev = false;
+ if (mdata->dock_cb)
+ mdata->dock_cb(MAX77693_MUIC_DOCK_DETACHED);
+ }
+ break;
+#if defined(CONFIG_MUIC_MAX77693_SUPPORT_SMART_DOCK)
+ case CABLE_TYPE_SMARTDOCK_MUIC:
+ case CABLE_TYPE_SMARTDOCK_TA_MUIC:
+ case CABLE_TYPE_SMARTDOCK_USB_MUIC:
+ if (adc != ADC_SMARTDOCK) {
+ dev_warn(info->dev, "%s: assume smartdock detach\n",
+ __func__);
+
+ max77693_muic_detach_smart_dock(info);
+ }
+ break;
+#endif /* CONFIG_MUIC_MAX77693_SUPPORT_SMART_DOCK */
+ case CABLE_TYPE_AUDIODOCK_MUIC:
+ if (adc != ADC_GND) {
+ dev_warn(info->dev, "%s: assume audiodock detach\n",
+ __func__);
+ info->cable_type = CABLE_TYPE_NONE_MUIC;
+
+ max77693_muic_set_charging_type(info, false);
+ info->is_adc_open_prev = false;
+
+ if (mdata->usb_cb && info->is_usb_ready)
+ mdata->usb_cb(USB_OTGHOST_DETACHED);
+
+ if (mdata->dock_cb)
+ mdata->dock_cb(MAX77693_MUIC_DOCK_DETACHED);
+ }
+ break;
+
+ default:
+ break;
}
/* 1Kohm ID regiter detection (mHL)
@@ -1569,13 +2075,22 @@ static int max77693_muic_handle_attach(struct max77693_muic_info *info,
}
return 0;
}
+#if !defined(CONFIG_MUIC_MAX77693_SEPARATE_MHL_PORT)
max77693_muic_attach_mhl(info, chgtyp);
+#endif
return 0;
}
switch (adc) {
case ADC_GND:
if (chgtyp == CHGTYP_NO_VOLTAGE) {
+#if defined(CONFIG_MUIC_MAX77693_SUPPORT_OTG_AUDIO_DOCK)
+ if (info->cable_type == CABLE_TYPE_AUDIODOCK_MUIC) {
+ pr_info("%s:%s audio_dock attached, ignore\n",
+ DEV_NAME, __func__);
+ break;
+ }
+#endif /* CONFIG_MUIC_MAX77693_SUPPORT_OTG_AUDIO_DOCK */
if (info->cable_type == CABLE_TYPE_OTG_MUIC) {
dev_info(info->dev,
"%s: duplicated(OTG)\n", __func__);
@@ -1583,12 +2098,10 @@ static int max77693_muic_handle_attach(struct max77693_muic_info *info,
}
info->cable_type = CABLE_TYPE_OTG_MUIC;
+ max77693_muic_set_usb_path(info, AP_USB_MODE);
+ msleep(40);
if (mdata->usb_cb && info->is_usb_ready)
mdata->usb_cb(USB_OTGHOST_ATTACHED);
-
- msleep(40);
-
- max77693_muic_set_usb_path(info, AP_USB_MODE);
} else if (chgtyp == CHGTYP_USB ||
chgtyp == CHGTYP_DOWNSTREAM_PORT ||
chgtyp == CHGTYP_DEDICATED_CHGR ||
@@ -1598,31 +2111,15 @@ static int max77693_muic_handle_attach(struct max77693_muic_info *info,
ret = max77693_muic_set_charging_type(info, false);
}
break;
+#if defined(CONFIG_MUIC_MAX77693_SUPPORT_SMART_DOCK)
case ADC_SMARTDOCK:
- if (info->cable_type == CABLE_TYPE_SMARTDOCK_MUIC) {
- dev_info(info->dev,
- "%s: duplicated(SMARTDOCK)\n", __func__);
- break;
- }
- dev_info(info->dev, "func:%s Attach SmartDock\n", __func__);
- info->cable_type = CABLE_TYPE_SMARTDOCK_MUIC;
- max77693_muic_set_usb_path(info, AP_USB_MODE);
- msleep(40);
- if (mdata->usb_cb && info->is_usb_ready)
- mdata->usb_cb(USB_POWERED_HOST_ATTACHED);
-#ifdef CONFIG_EXTCON
- if (info->edev && info->is_mhl_ready)
- extcon_set_cable_state(info->edev, "MHL", true);
-#else
- if (mdata->mhl_cb && info->is_mhl_ready)
- mdata->mhl_cb(MAX77693_MUIC_ATTACHED);
-#endif
- max77693_muic_set_charging_type(info, false);
+ max77693_muic_attach_smart_dock(info, adc, vbvolt, chgtyp);
break;
+#endif /* CONFIG_MUIC_MAX77693_SUPPORT_SMART_DOCK */
case ADC_JIG_UART_OFF:
max77693_muic_handle_jig_uart(info, vbvolt);
-#if defined(CONFIG_SEC_MODEM_M0_TD)
- gpio_set_value(GPIO_AP_CP_INT1, GPIO_LEVEL_HIGH);
+#if defined(CONFIG_MACH_T0_CHN_CMCC)
+ gpio_set_value(GPIO_AP_TD_INT1, GPIO_LEVEL_HIGH);
#endif
mdata->jig_state(true);
break;
@@ -1632,8 +2129,8 @@ static int max77693_muic_handle_attach(struct max77693_muic_info *info,
dev_info(info->dev, "%s: SKIP_JIG_USB\n", __func__);
ret = max77693_muic_attach_usb_type(info, adc);
}
-#if defined(CONFIG_SEC_MODEM_M0_TD)
- gpio_set_value(GPIO_AP_CP_INT1, GPIO_LEVEL_HIGH);
+#if defined(CONFIG_MACH_T0_CHN_CMCC)
+ gpio_set_value(GPIO_AP_TD_INT1, GPIO_LEVEL_HIGH);
#endif
mdata->jig_state(true);
break;
@@ -1653,6 +2150,25 @@ static int max77693_muic_handle_attach(struct max77693_muic_info *info,
* we do charging at CARDOCK.
*/
break;
+#if defined(CONFIG_MUIC_DET_JACK)
+ case ADC_MHL_OR_SENDEND:
+ case ADC_DOCK_VOL_UP:
+ case ADC_DOCK_VOL_DN:
+ if ((!adc1k) && (info->cable_type == CABLE_TYPE_EARJACK_MUIC)) {
+ info->earkeypressed = true;
+ max77693_muic_press_earjack_key(info, adc);
+ }
+ break;
+ case ADC_EARJACK:
+ if ((info->cable_type == CABLE_TYPE_EARJACK_MUIC)
+ && (info->earkeypressed)) {
+ info->earkeypressed = false;
+ max77693_muic_press_earjack_key(info, adc);
+ } else {
+ max77693_muic_attach_earjack(info, adc);
+ }
+ break;
+#endif
case ADC_CEA936ATYPE1_CHG:
case ADC_CEA936ATYPE2_CHG:
case ADC_OPEN:
@@ -1696,6 +2212,14 @@ static int max77693_muic_handle_attach(struct max77693_muic_info *info,
info->cable_type = CABLE_TYPE_NONE_MUIC;
break;
default:
+ if (dxovp) {
+ dev_info(info->dev, "%s:TA(DXOVP)\n", __func__);
+ info->cable_type = CABLE_TYPE_TA_MUIC;
+ ret = max77693_muic_set_charging_type(info,
+ false);
+ if (ret)
+ info->cable_type = CABLE_TYPE_NONE_MUIC;
+ }
break;
}
break;
@@ -1704,6 +2228,15 @@ static int max77693_muic_handle_attach(struct max77693_muic_info *info,
adc);
break;
}
+
+#ifdef CONFIG_FAST_BOOT
+ if ((info->cable_type == CABLE_TYPE_TA_MUIC) && (fake_shut_down)) {
+ pr_info("%s: Resetting the device in fake shutdown mode"\
+ "(TA inserted !!!)\n", __func__);
+ kernel_power_off();
+ }
+#endif
+
return ret;
}
@@ -1732,7 +2265,7 @@ static int max77693_muic_handle_detach(struct max77693_muic_info *info, int irq)
max77693_read_reg(client, MAX77693_MUIC_REG_CTRL2, &cntl2_val);
dev_info(info->dev, "%s: CNTL2(0x%02x)\n", __func__, cntl2_val);
-#if defined(CONFIG_MACH_M0_CTC)
+#if defined(CONFIG_MACH_M0_CTC) || defined(CONFIG_MACH_T0_CHN_CTC)
if (cp_usb_state != 0) {
if (system_rev < 11) {
gpio_direction_output(GPIO_USB_BOOT_EN, 0);
@@ -1745,6 +2278,9 @@ static int max77693_muic_handle_detach(struct max77693_muic_info *info, int irq)
cp_usb_state = 0;
}
#endif
+#if defined(CONFIG_SWITCH_DUAL_MODEM) && defined(CONFIG_MACH_T0_CHN_CMCC)
+ gpio_direction_output(GPIO_CP_USB_ON, 0);
+#endif
#ifdef CONFIG_USBHUB_USB3803
/* setting usb hub in default mode (standby) */
@@ -1769,6 +2305,18 @@ static int max77693_muic_handle_detach(struct max77693_muic_info *info, int irq)
if (mdata->usb_cb && info->is_usb_ready)
mdata->usb_cb(USB_OTGHOST_DETACHED);
break;
+
+ case CABLE_TYPE_AUDIODOCK_MUIC:
+ dev_info(info->dev, "%s: AUDIO\n", __func__);
+ info->cable_type = CABLE_TYPE_NONE_MUIC;
+
+ if (mdata->usb_cb && info->is_usb_ready)
+ mdata->usb_cb(USB_OTGHOST_DETACHED);
+
+ if (mdata->dock_cb)
+ mdata->dock_cb(MAX77693_MUIC_DOCK_DETACHED);
+ break;
+
case CABLE_TYPE_USB_MUIC:
case CABLE_TYPE_JIG_USB_OFF_MUIC:
case CABLE_TYPE_JIG_USB_ON_MUIC:
@@ -1802,8 +2350,8 @@ static int max77693_muic_handle_detach(struct max77693_muic_info *info, int irq)
info->cable_type = CABLE_TYPE_DESKDOCK_MUIC;
break;
}
- if (mdata->deskdock_cb)
- mdata->deskdock_cb(MAX77693_MUIC_DETACHED);
+ if (mdata->dock_cb)
+ mdata->dock_cb(MAX77693_MUIC_DOCK_DETACHED);
break;
case CABLE_TYPE_CARDOCK_MUIC:
dev_info(info->dev, "%s: CARDOCK\n", __func__);
@@ -1814,8 +2362,8 @@ static int max77693_muic_handle_detach(struct max77693_muic_info *info, int irq)
info->cable_type = CABLE_TYPE_CARDOCK_MUIC;
break;
}
- if (mdata->cardock_cb)
- mdata->cardock_cb(MAX77693_MUIC_DETACHED);
+ if (mdata->dock_cb)
+ mdata->dock_cb(MAX77693_MUIC_DOCK_DETACHED);
break;
case CABLE_TYPE_TA_MUIC:
#ifdef CONFIG_EXTCON
@@ -1836,21 +2384,13 @@ static int max77693_muic_handle_detach(struct max77693_muic_info *info, int irq)
dev_info(info->dev, "%s: JIG UART/BOOTOFF\n", __func__);
info->cable_type = CABLE_TYPE_NONE_MUIC;
break;
+#if defined(CONFIG_MUIC_MAX77693_SUPPORT_SMART_DOCK)
case CABLE_TYPE_SMARTDOCK_MUIC:
- dev_info(info->dev, "%s: SMARTDOCK\n", __func__);
- info->cable_type = CABLE_TYPE_NONE_MUIC;
- if (mdata->usb_cb && info->is_usb_ready)
- mdata->usb_cb(USB_POWERED_HOST_DETACHED);
- ret = max77693_muic_set_charging_type(info, false);
-#ifdef CONFIG_EXTCON
- if (info->edev && info->is_mhl_ready)
- extcon_set_cable_state(info->edev, "MHL", false);
-#else
- if (mdata->mhl_cb && info->is_mhl_ready)
- mdata->mhl_cb(MAX77693_MUIC_DETACHED);
-#endif
- max77693_muic_set_charging_type(info, false);
+ case CABLE_TYPE_SMARTDOCK_TA_MUIC:
+ case CABLE_TYPE_SMARTDOCK_USB_MUIC:
+ max77693_muic_detach_smart_dock(info);
break;
+#endif /* CONFIG_MUIC_MAX77693_SUPPORT_SMART_DOCK */
case CABLE_TYPE_JIG_UART_OFF_VB_MUIC:
dev_info(info->dev, "%s: JIG UART/OFF/VB\n", __func__);
info->cable_type = CABLE_TYPE_NONE_MUIC;
@@ -1893,6 +2433,15 @@ static int max77693_muic_handle_detach(struct max77693_muic_info *info, int irq)
mdata->mhl_cb(MAX77693_MUIC_DETACHED);
#endif
break;
+#if defined(CONFIG_MUIC_DET_JACK)
+ case CABLE_TYPE_EARJACK_MUIC:
+ dev_info(info->dev, "%s: EARJACK\n", __func__);
+ info->cable_type = CABLE_TYPE_NONE_MUIC;
+
+ if (mdata->earjack_cb)
+ mdata->earjack_cb(MAX77693_MUIC_DETACHED);
+ break;
+#endif
case CABLE_TYPE_UNKNOWN_MUIC:
dev_info(info->dev, "%s: UNKNOWN\n", __func__);
info->cable_type = CABLE_TYPE_NONE_MUIC;
@@ -1909,20 +2458,112 @@ static int max77693_muic_handle_detach(struct max77693_muic_info *info, int irq)
/* jig state clear */
mdata->jig_state(false);
-#if defined(CONFIG_SEC_MODEM_M0_TD)
- gpio_set_value(GPIO_AP_CP_INT1, GPIO_LEVEL_LOW);
+#if defined(CONFIG_MACH_T0_CHN_CMCC)
+ gpio_set_value(GPIO_AP_TD_INT1, GPIO_LEVEL_LOW);
#endif
return ret;
}
+static int max77693_muic_filter_dev(struct max77693_muic_info *info,
+ u8 status1, u8 status2)
+{
+ u8 adc, adclow, adcerr, adc1k, chgtyp, vbvolt, dxovp;
+ int intr = INT_ATTACH;
+
+ adc = status1 & STATUS1_ADC_MASK;
+ adclow = status1 & STATUS1_ADCLOW_MASK;
+ adcerr = status1 & STATUS1_ADCERR_MASK;
+ adc1k = status1 & STATUS1_ADC1K_MASK;
+ chgtyp = status2 & STATUS2_CHGTYP_MASK;
+ vbvolt = status2 & STATUS2_VBVOLT_MASK;
+ dxovp = status2 & STATUS2_DXOVP_MASK;
+
+ dev_info(info->dev, "adc:%x adcerr:%x chgtyp:%x vb:%x dxovp:%x cable_type:%x\n",
+ adc, adcerr, chgtyp, vbvolt, dxovp, info->cable_type);
+
+#if !defined(CONFIG_MUIC_MAX77693_SEPARATE_MHL_PORT)
+ if (adclow && adc1k) {
+ pr_info("%s:%s MHL cable connected\n", DEV_NAME, __func__);
+ return INT_ATTACH;
+ }
+#endif /* CONFIG_MUIC_MAX77693_SEPARATE_MHL_PORT */
+
+ switch (adc) {
+#if defined(CONFIG_MACH_GC1)
+ case ADC_GND:
+ if (info->is_otg_attach_blocked) {
+ pr_warn("%s:%s otg attach is blocked, ignore\n",
+ DEV_NAME, __func__);
+ return -1;
+ }
+ break;
+ case ADC_MHL ... (ADC_CEA936ATYPE1_CHG - 1):
+ case ADC_DESKDOCK:
+ case ADC_CARDOCK ... (ADC_OPEN - 1):
+#else
+ case ADC_GND:
+ if (!adclow) {
+ pr_info("%s:%s ADC_GND & !adclow = OTG\n", DEV_NAME,
+ __func__);
+ break;
+ }
+ pr_info("%s:%s ADC_GND & adclow != OTG\n", DEV_NAME,
+ __func__);
+#if !defined(CONFIG_MUIC_DET_JACK)
+ case ADC_MHL ... (ADC_SMARTDOCK - 1):
+ case (ADC_OPEN - 1):
+#endif /* !CONFIG_MUIC_DET_JACK */
+ case (ADC_SMARTDOCK + 1) ... (ADC_CEA936ATYPE1_CHG - 1):
+#if !defined(CONFIG_MUIC_MAX77693_SUPPORT_CAR_DOCK)
+ case ADC_CARDOCK:
+#endif /* CONFIG_MUIC_MAX77693_SUPPORT_CAR_DOCK */
+#endif /* CONFIG_MACH_GC1 */
+ dev_warn(info->dev, "%s: unsupported ADC(0x%02x)\n",
+ __func__, adc);
+ intr = INT_DETACH;
+ break;
+ case ADC_OPEN:
+ if (!adcerr) {
+ if (chgtyp == CHGTYP_NO_VOLTAGE) {
+ if (dxovp)
+ break;
+ else
+ intr = INT_DETACH;
+ } else if (chgtyp == CHGTYP_USB ||
+ chgtyp == CHGTYP_DOWNSTREAM_PORT ||
+ chgtyp == CHGTYP_DEDICATED_CHGR ||
+ chgtyp == CHGTYP_500MA ||
+ chgtyp == CHGTYP_1A) {
+ switch (info->cable_type) {
+ case CABLE_TYPE_OTG_MUIC:
+ case CABLE_TYPE_DESKDOCK_MUIC:
+ case CABLE_TYPE_CARDOCK_MUIC:
+ case CABLE_TYPE_SMARTDOCK_MUIC:
+ case CABLE_TYPE_SMARTDOCK_TA_MUIC:
+ case CABLE_TYPE_SMARTDOCK_USB_MUIC:
+ case CABLE_TYPE_AUDIODOCK_MUIC:
+ intr = INT_DETACH;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ return intr;
+}
+
static void max77693_muic_detect_dev(struct max77693_muic_info *info, int irq)
{
struct i2c_client *client = info->muic;
u8 status[2];
- u8 adc, chgtyp, adcerr;
- int intr = INT_ATTACH;
int ret;
u8 cntl1_val;
+ int intr = INT_ATTACH;
ret = max77693_read_reg(client, MAX77693_MUIC_REG_CTRL1, &cntl1_val);
dev_info(info->dev, "func:%s CONTROL1:%x\n", __func__, cntl1_val);
@@ -1938,40 +2579,28 @@ static void max77693_muic_detect_dev(struct max77693_muic_info *info, int irq)
dev_info(info->dev, "%s: STATUS1:0x%x, 2:0x%x\n", __func__,
status[0], status[1]);
+#if !defined(CONFIG_MACH_GC1)
if ((irq == info->irq_adc) &&
max77693_muic_handle_dock_vol_key(info, status[0])) {
dev_info(info->dev,
"max77693_muic_handle_dock_vol_key(irq_adc:%x)", irq);
return;
}
+#endif
- adc = status[0] & STATUS1_ADC_MASK;
- adcerr = status[0] & STATUS1_ADCERR_MASK;
- chgtyp = status[1] & STATUS2_CHGTYP_MASK;
-
- dev_info(info->dev, "adc:%x adcerr:%x chgtyp:%xcable_type:%x\n", adc,
- adcerr, chgtyp, info->cable_type);
-
- if (!adcerr && adc == ADC_OPEN) {
- if (chgtyp == CHGTYP_NO_VOLTAGE)
- intr = INT_DETACH;
- else if (chgtyp == CHGTYP_USB ||
- chgtyp == CHGTYP_DOWNSTREAM_PORT ||
- chgtyp == CHGTYP_DEDICATED_CHGR ||
- chgtyp == CHGTYP_500MA || chgtyp == CHGTYP_1A) {
- if (info->cable_type == CABLE_TYPE_OTG_MUIC ||
- info->cable_type == CABLE_TYPE_DESKDOCK_MUIC ||
- info->cable_type == CABLE_TYPE_CARDOCK_MUIC)
- intr = INT_DETACH;
- }
- }
+ wake_lock_timeout(&info->muic_wake_lock, HZ * 2);
+
+ intr = max77693_muic_filter_dev(info, status[0], status[1]);
if (intr == INT_ATTACH) {
dev_info(info->dev, "%s: ATTACHED\n", __func__);
max77693_muic_handle_attach(info, status[0], status[1], irq);
- } else {
+ } else if (intr == INT_DETACH) {
dev_info(info->dev, "%s: DETACHED\n", __func__);
max77693_muic_handle_detach(info, irq);
+ } else {
+ pr_info("%s:%s device filtered, nothing affect.\n", DEV_NAME,
+ __func__);
}
return;
}
@@ -1990,7 +2619,7 @@ static irqreturn_t max77693_muic_irq(int irq, void *data)
#define REQUEST_IRQ(_irq, _name) \
do { \
ret = request_threaded_irq(_irq, NULL, max77693_muic_irq, \
- 0, _name, info); \
+ IRQF_NO_SUSPEND, _name, info); \
if (ret < 0) \
dev_err(info->dev, "Failed to request IRQ #%d: %d\n", \
_irq, ret); \
@@ -2091,7 +2720,6 @@ static void max77693_muic_usb_detect(struct work_struct *work)
mdata->usb_cb(USB_OTGHOST_ATTACHED);
break;
case CABLE_TYPE_SMARTDOCK_MUIC:
- mdata->usb_cb(USB_POWERED_HOST_ATTACHED);
break;
default:
break;
@@ -2101,6 +2729,7 @@ static void max77693_muic_usb_detect(struct work_struct *work)
mutex_unlock(&info->mutex);
}
+#if !defined(CONFIG_MUIC_MAX77693_SEPARATE_MHL_PORT)
static void max77693_muic_mhl_detect(struct work_struct *work)
{
struct max77693_muic_info *info =
@@ -2124,11 +2753,14 @@ static void max77693_muic_mhl_detect(struct work_struct *work)
}
mutex_unlock(&info->mutex);
}
+#endif /* !CONFIG_MUIC_MAX77693_SEPARATE_MHL_PORT */
static int uart_switch_init(struct max77693_muic_info *info)
{
int ret, val;
+#if !defined(CONFIG_MACH_T0) && !defined(CONFIG_MACH_M3) \
+ && !defined(CONFIG_MACH_SLP_T0_LTE)
if (info->max77693->pmic_rev < MAX77693_REV_PASS2) {
ret = gpio_request(GPIO_UART_SEL, "UART_SEL");
if (ret < 0) {
@@ -2140,6 +2772,7 @@ static int uart_switch_init(struct max77693_muic_info *info)
gpio_direction_output(GPIO_UART_SEL, val);
pr_info("func: %s uart_gpio val: %d\n", __func__, val);
}
+#endif /* !defined(CONFIG_MACH_T0) */
#ifdef CONFIG_LTE_VIA_SWITCH
ret = gpio_request(GPIO_LTE_VIA_UART_SEL, "LTE_VIA_SEL");
if (ret < 0) {
@@ -2157,6 +2790,32 @@ static int uart_switch_init(struct max77693_muic_info *info)
gpio_export_link(switch_dev, "uart_sel", GPIO_UART_SEL);
#endif
*/
+#if defined(CONFIG_SWITCH_DUAL_MODEM)
+ ret = gpio_request(GPIO_UART_SEL, "UART_SEL");
+ if (ret < 0) {
+ pr_err("Failed to request GPIO_UART_SEL!\n");
+ return -ENODEV;
+ }
+ val = gpio_get_value(GPIO_UART_SEL);
+ gpio_direction_output(GPIO_UART_SEL, val);
+
+ ret = gpio_request(GPIO_USB_SEL, "USB_SEL");
+ if (ret < 0) {
+ pr_err("Failed to request GPIO_USB_SEL!\n");
+ return -ENODEV;
+ }
+ val = gpio_get_value(GPIO_USB_SEL);
+ gpio_direction_output(GPIO_USB_SEL, val);
+
+#if defined(CONFIG_MACH_T0_CHN_CMCC)
+ ret = gpio_request(GPIO_CP_USB_ON, "CP_USB_ON");
+ if (ret < 0) {
+ pr_err("Failed to request GPIO_CP_USB_ON!\n");
+ return -ENODEV;
+ }
+ gpio_direction_output(GPIO_CP_USB_ON, 0);
+#endif
+#endif
return 0;
}
@@ -2283,6 +2942,12 @@ static int __devinit max77693_muic_probe(struct platform_device *pdev)
info->irq_adc1k = max77693->irq_base + MAX77693_MUIC_IRQ_INT1_ADC1K;
info->muic_data = pdata->muic;
info->is_adc_open_prev = true;
+#if defined(CONFIG_MACH_GC1)
+ info->is_otg_attach_blocked = false;
+#endif /* CONFIG_MACH_GC1 */
+
+ wake_lock_init(&info->muic_wake_lock, WAKE_LOCK_SUSPEND,
+ "muic wake lock");
if (pdata->is_default_uart_path_cp)
info->is_default_uart_path_cp =
@@ -2291,6 +2956,10 @@ static int __devinit max77693_muic_probe(struct platform_device *pdev)
info->is_default_uart_path_cp = false;
info->cable_type = CABLE_TYPE_UNKNOWN_MUIC;
info->muic_data->sw_path = AP_USB_MODE;
+#if defined(CONFIG_MUIC_DET_JACK)
+ info->earkeypressed = false;
+ info->previous_earkey = 0;
+#endif
gInfo = info;
platform_set_drvdata(pdev, info);
@@ -2354,6 +3023,24 @@ static int __devinit max77693_muic_probe(struct platform_device *pdev)
} else if (max77693->pmic_rev >= MAX77693_REV_PASS2) {
/*PASS2 */
int switch_sel = get_switch_sel();
+#if defined(CONFIG_SWITCH_DUAL_MODEM)
+ switch_sel &= 0xf;
+ if ((switch_sel & MAX77693_SWITCH_SEL_1st_BIT_USB) == 0x1)
+ info->muic_data->sw_path = AP_USB_MODE;
+ else if ((switch_sel & MAX77693_SWITCH_SEL_1st_BIT_USB) == 0x2)
+ info->muic_data->sw_path = CP_ESC_USB_MODE;
+ else
+ info->muic_data->sw_path = CP_USB_MODE;
+
+ if ((switch_sel & MAX77693_SWITCH_SEL_2nd_BIT_UART)
+ == 0x1 << 2)
+ info->muic_data->uart_path = UART_PATH_AP;
+ else if ((switch_sel & MAX77693_SWITCH_SEL_2nd_BIT_UART)
+ == 0x2 << 2)
+ info->muic_data->uart_path = UART_PATH_CP_ESC;
+ else
+ info->muic_data->uart_path = UART_PATH_CP;
+#else
if (switch_sel & MAX77693_SWITCH_SEL_1st_BIT_USB)
info->muic_data->sw_path = AP_USB_MODE;
else
@@ -2367,6 +3054,7 @@ static int __devinit max77693_muic_probe(struct platform_device *pdev)
info->muic_data->uart_path = UART_PATH_LTE;
#endif
}
+#endif
pr_info("%s: switch_sel: %x\n", __func__, switch_sel);
}
/* create sysfs group */
@@ -2420,8 +3108,10 @@ static int __devinit max77693_muic_probe(struct platform_device *pdev)
INIT_DELAYED_WORK(&info->usb_work, max77693_muic_usb_detect);
schedule_delayed_work(&info->usb_work, msecs_to_jiffies(17000));
+#if !defined(CONFIG_MUIC_MAX77693_SEPARATE_MHL_PORT)
INIT_DELAYED_WORK(&info->mhl_work, max77693_muic_mhl_detect);
schedule_delayed_work(&info->mhl_work, msecs_to_jiffies(25000));
+#endif
return 0;
@@ -2438,6 +3128,7 @@ static int __devinit max77693_muic_probe(struct platform_device *pdev)
err_input:
platform_set_drvdata(pdev, NULL);
input_free_device(input);
+ wake_lock_destroy(&info->muic_wake_lock);
err_kfree:
kfree(info);
err_return:
@@ -2459,6 +3150,7 @@ static int __devexit max77693_muic_remove(struct platform_device *pdev)
free_irq(info->irq_chgtype, info);
free_irq(info->irq_vbvolt, info);
free_irq(info->irq_adc1k, info);
+ wake_lock_destroy(&info->muic_wake_lock);
#ifndef CONFIG_TARGET_LOCALE_NA
gpio_free(info->muic_data->gpio_usb_sel);
#endif /* CONFIG_TARGET_LOCALE_NA */
diff --git a/drivers/misc/max8893.c b/drivers/misc/max8893.c
new file mode 100644
index 0000000..9a3836a
--- /dev/null
+++ b/drivers/misc/max8893.c
@@ -0,0 +1,160 @@
+/*
+*
+*Minimalistic MAX8893 PMIC driver for CMC732 WiMAX chipset
+*
+*Use retrys when bus is shared or problematic.
+*
+*Functions not exported are made static
+*
+*In this driver, BUCK is referred as LDO number "-1"
+*/
+
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/wimax/samsung/max8893.h>
+
+/*#define WIMAX_DEBUG*/
+
+
+static struct i2c_client *max8893_client;
+
+
+
+static int max8893_set_ldo(int ldo, int enable)
+{
+ int temp;
+ temp = i2c_smbus_read_byte_data(max8893_client, MAX8893_REG_ONOFF);
+ if (temp < 0)
+ return temp;
+ temp = enable ? (temp|ENABLE_LDO(ldo)) : (temp&DISABLE_LDO(ldo));
+ return i2c_smbus_write_byte_data(max8893_client,
+ MAX8893_REG_ONOFF, (unsigned char)temp);
+
+}
+
+#ifdef WIMAX_DEBUG
+
+static int max8893_get_ldo(int ldo)
+{
+ int temp;
+ temp = i2c_smbus_read_byte_data(max8893_client, MAX8893_REG_ONOFF);
+ if (temp < 0)
+ return temp;
+ return (temp&ENABLE_LDO(ldo)) ? 1 : 0;
+}
+
+static int max8893_get_voltage(int ldo)
+{
+ int voltage;
+ voltage = i2c_smbus_read_byte_data(max8893_client,
+ MAX8893_REG_LDO(ldo));
+ return voltage ;
+
+}
+
+#endif
+
+static int max8893_set_voltage(int ldo, int mv)
+{
+ int temp;
+ if (mv > MAX_VOLTAGE(ldo))
+ return -22;
+ temp = MIN_VOLTAGE(ldo);
+ if (mv < temp)
+ return -22;
+ temp = (mv - temp)/100;
+ return i2c_smbus_write_byte_data(max8893_client,
+ MAX8893_REG_LDO(ldo), (unsigned char)temp);
+}
+
+int wimax_pmic_set_voltage(void)
+{
+ int ret;
+#ifdef WIMAX_DEBUG
+ int i;
+#endif
+
+ mdelay(10);
+ ret = (
+ max8893_set_voltage(BUCK, 1200)|
+ max8893_set_voltage(LDO1, 2800)|
+ max8893_set_voltage(LDO2, 2800)|
+ max8893_set_voltage(LDO3, 3300)|
+ max8893_set_voltage(LDO4, 2900)|
+ max8893_set_voltage(LDO5, 2800)
+ );
+ if (ret)
+ printk(KERN_ERR "ERROR SETTING WIMAX PMIC POWER ret = %d\n",
+ ret);
+
+#ifdef WIMAX_DEBUG
+ max8893_set_ldo(BUCK, ON);
+ max8893_set_ldo(LDO1, ON);
+ max8893_set_ldo(LDO2, ON);
+ max8893_set_ldo(LDO3, ON);
+ max8893_set_ldo(LDO4, ON);
+ max8893_set_ldo(LDO5, ON);
+
+ for (i = 1; i < 6; i++) {
+ printk(KERN_DEBUG "[WIMAX_DEBUG] LDO%d STATE :%d\n", i,
+ max8893_get_ldo(i));
+ printk(KERN_DEBUG "[WIMAX_DEBUG] LDO%d VOLTAGE:%x\n", i,
+ max8893_get_voltage(i));
+ }
+#endif
+ return ret;
+}
+EXPORT_SYMBOL(wimax_pmic_set_voltage);
+
+static int max8893_wmx_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int ret;
+#ifdef WIMAX_DEBUG
+ int i;
+#endif
+ max8893_client = client;
+ return 0;
+}
+
+
+static int max8893_wmx_remove(struct i2c_client *client)
+{
+ max8893_set_ldo(BUCK, OFF);
+ max8893_set_ldo(LDO1, OFF);
+ max8893_set_ldo(LDO2, OFF);
+ max8893_set_ldo(LDO3, OFF);
+ max8893_set_ldo(LDO4, OFF);
+ max8893_set_ldo(LDO5, OFF);
+ return 0;
+}
+const struct i2c_device_id max8893_wmx_id[] = {
+ { "max8893_wmx", 0 },
+ { }
+};
+
+MODULE_DEVICE_TABLE(i2c, max8893_wmx_id);
+static struct i2c_driver max8893_wmx_driver = {
+ .probe = max8893_wmx_probe,
+ .remove = max8893_wmx_remove,
+ .id_table = max8893_wmx_id,
+ .driver = {
+ .name = "max8893_wmx",
+ },
+};
+static int __init max8893_wmx_pmic_init(void)
+{
+ return i2c_add_driver(&max8893_wmx_driver);
+}
+static void __exit max8893_wmx_pmic_exit(void)
+{
+ i2c_del_driver(&max8893_wmx_driver);
+}
+subsys_initcall(max8893_wmx_pmic_init);
+module_exit(max8893_wmx_pmic_exit);
+MODULE_DESCRIPTION("MAXIM 8893 PMIC driver for WiMAX");
+MODULE_AUTHOR("SAMSUNG ELECTRONICS");
+MODULE_LICENSE("GPL");
diff --git a/drivers/misc/max8997-muic.c b/drivers/misc/max8997-muic.c
index 3321545..202df3d 100644
--- a/drivers/misc/max8997-muic.c
+++ b/drivers/misc/max8997-muic.c
@@ -35,7 +35,10 @@
#ifdef CONFIG_USBHUB_USB3803
#include <linux/usb3803.h>
#endif
-
+#ifdef CONFIG_EXTCON
+#include <linux/extcon.h>
+#define DEV_NAME "max8997-muic"
+#endif
/* MAX8997 STATUS1 register */
#define STATUS1_ADC_SHIFT 0
@@ -83,6 +86,13 @@
#define INT_DETACH (0x1 << 1)
#define INT_ATTACH (0x1 << 0)
+enum {
+ CTRL1_TO_OPEN = 0x0,
+ CTRL1_TO_USB = 0x1,
+ CTRL1_TO_AUDIO = 0x2,
+ CTRL1_TO_UART = 0x3,
+};
+
/* MAX8997 MUIC CHG_TYP setting values */
enum {
/* No Valid voltage at VB (Vvb < Vvbdet) */
@@ -151,7 +161,8 @@ struct max8997_muic_info {
int irq_adcerr;
int mansw;
- enum cable_type cable_type;
+ enum muic_acc_type acc_type;
+ enum muic_chg_type chg_type;
struct delayed_work init_work;
struct delayed_work usb_work;
struct delayed_work mhl_work;
@@ -160,13 +171,16 @@ struct max8997_muic_info {
int irq_chgins;
int irq_chgrm;
bool is_ovp_state;
-#endif
+#endif /* CONFIG_MUIC_MAX8997_OVPUI */
bool is_usb_ready;
bool is_mhl_ready;
struct input_dev *input;
int previous_key;
+#ifdef CONFIG_EXTCON
+ struct extcon_dev *edev;
+#endif
};
#if 0
@@ -194,11 +208,12 @@ static ssize_t max8997_muic_show_usb_state(struct device *dev,
{
struct max8997_muic_info *info = dev_get_drvdata(dev);
- switch (info->cable_type) {
- case CABLE_TYPE_USB:
- case CABLE_TYPE_JIG_USB_OFF:
- case CABLE_TYPE_JIG_USB_ON:
- return sprintf(buf, "USB_STATE_CONFIGURED\n");
+ switch (info->acc_type) {
+ case MUIC_ACC_TYPE_USB:
+ case MUIC_ACC_TYPE_JIG_USB_OFF:
+ case MUIC_ACC_TYPE_JIG_USB_ON:
+ if (info->chg_type == MUIC_CHG_TYPE_USB)
+ return sprintf(buf, "USB_STATE_CONFIGURED\n");
default:
break;
}
@@ -212,33 +227,35 @@ static ssize_t max8997_muic_show_device(struct device *dev,
{
struct max8997_muic_info *info = dev_get_drvdata(dev);
- switch (info->cable_type) {
- case CABLE_TYPE_NONE:
+ switch (info->acc_type) {
+ case MUIC_ACC_TYPE_NONE:
return sprintf(buf, "No cable\n");
- case CABLE_TYPE_USB:
+ case MUIC_ACC_TYPE_USB:
return sprintf(buf, "USB\n");
- case CABLE_TYPE_OTG:
+ case MUIC_ACC_TYPE_OTG:
return sprintf(buf, "OTG\n");
- case CABLE_TYPE_TA:
+ case MUIC_ACC_TYPE_TA:
return sprintf(buf, "TA\n");
- case CABLE_TYPE_DESKDOCK:
+ case MUIC_ACC_TYPE_DESKDOCK:
return sprintf(buf, "Desk Dock\n");
- case CABLE_TYPE_CARDOCK:
+ case MUIC_ACC_TYPE_CARDOCK:
return sprintf(buf, "Car Dock\n");
- case CABLE_TYPE_JIG_UART_OFF:
- return sprintf(buf, "JIG UART OFF\n");
- case CABLE_TYPE_JIG_UART_OFF_VB:
- return sprintf(buf, "JIG UART OFF/VB\n");
- case CABLE_TYPE_JIG_UART_ON:
+ case MUIC_ACC_TYPE_JIG_UART_OFF:
+ if (info->chg_type == MUIC_CHG_TYPE_TA)
+ return sprintf(buf, "JIG UART OFF VB\n");
+ else
+ return sprintf(buf, "JIG UART OFF\n");
+ case MUIC_ACC_TYPE_JIG_UART_ON:
return sprintf(buf, "JIG UART ON\n");
- case CABLE_TYPE_JIG_USB_OFF:
+ case MUIC_ACC_TYPE_JIG_USB_OFF:
return sprintf(buf, "JIG USB OFF\n");
- case CABLE_TYPE_JIG_USB_ON:
+ case MUIC_ACC_TYPE_JIG_USB_ON:
return sprintf(buf, "JIG USB ON\n");
- case CABLE_TYPE_MHL:
- return sprintf(buf, "mHL\n");
- case CABLE_TYPE_MHL_VB:
- return sprintf(buf, "mHL charging\n");
+ case MUIC_ACC_TYPE_MHL:
+ if (info->chg_type == MUIC_CHG_TYPE_MHL_VB)
+ return sprintf(buf, "mHL charging\n");
+ else
+ return sprintf(buf, "mHL\n");
default:
break;
}
@@ -251,7 +268,7 @@ static ssize_t max8997_muic_show_manualsw(struct device *dev,
{
struct max8997_muic_info *info = dev_get_drvdata(dev);
- switch (info->muic_data->sw_path) {
+ switch (info->muic_data->usb_path) {
case AP_USB_MODE:
return sprintf(buf, "PDA\n");
case CP_USB_MODE:
@@ -272,10 +289,10 @@ static ssize_t max8997_muic_set_manualsw(struct device *dev,
struct max8997_muic_info *info = dev_get_drvdata(dev);
if (!strncasecmp(buf, "PDA", 3)) {
- info->muic_data->sw_path = AP_USB_MODE;
+ info->muic_data->usb_path = AP_USB_MODE;
dev_info(info->dev, "%s: AP_USB_MODE\n", __func__);
} else if (!strncasecmp(buf, "MODEM", 5)) {
- info->muic_data->sw_path = CP_USB_MODE;
+ info->muic_data->usb_path = CP_USB_MODE;
dev_info(info->dev, "%s: CP_USB_MODE\n", __func__);
} else
dev_warn(info->dev, "%s: Wrong command\n", __func__);
@@ -417,47 +434,6 @@ static void max8997_muic_set_adcdbset(struct max8997_muic_info *info,
dev_err(info->dev, "%s: fail to update reg\n", __func__);
}
-static ssize_t max8997_muic_show_adc_debounce_time(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct max8997_muic_info *info = dev_get_drvdata(dev);
- int ret;
- u8 val;
-
- if (!info->muic)
- return sprintf(buf, "No I2C client\n");
-
- ret = max8997_read_reg(info->muic, MAX8997_MUIC_REG_CTRL3, &val);
- if (ret) {
- dev_err(info->dev, "%s: fail to read muic reg\n", __func__);
- return sprintf(buf, "UNKNOWN\n");
- }
- val &= CTRL3_ADCDBSET_MASK;
- val = val >> CTRL3_ADCDBSET_SHIFT;
-
- return sprintf(buf, "%x\n", val);
-}
-
-static ssize_t max8997_muic_set_adc_debounce_time(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct max8997_muic_info *info = dev_get_drvdata(dev);
- int value;
-
- sscanf(buf, "%d", &value);
-
- value = (value & 0x3);
-
-#if 0
- max8997_muic_set_adcdbset(info, value);
-#else
- dev_info(info->dev, "%s: Do nothing\n", __func__);
-#endif
-
- return count;
-}
-
static ssize_t max8997_muic_show_status(struct device *dev,
struct device_attribute *attr,
char *buf)
@@ -530,9 +506,6 @@ static DEVICE_ATTR(audio_path, 0664,
max8997_muic_show_audio_path, max8997_muic_set_audio_path);
static DEVICE_ATTR(otg_test, 0664,
max8997_muic_show_otg_test, max8997_muic_set_otg_test);
-static DEVICE_ATTR(adc_debounce_time, 0664,
- max8997_muic_show_adc_debounce_time,
- max8997_muic_set_adc_debounce_time);
static DEVICE_ATTR(status, 0664,
max8997_muic_show_status, max8997_muic_set_status);
@@ -543,7 +516,6 @@ static struct attribute *max8997_muic_attributes[] = {
&dev_attr_adc.attr,
&dev_attr_audio_path.attr,
&dev_attr_otg_test.attr,
- &dev_attr_adc_debounce_time.attr,
&dev_attr_status.attr,
NULL
};
@@ -552,103 +524,882 @@ static const struct attribute_group max8997_muic_group = {
.attrs = max8997_muic_attributes,
};
-static int max8997_muic_set_usb_path(struct max8997_muic_info *info, int path)
+static int set_usb_sel(struct max8997_muic_info *info, int path)
+{
+ int gpio_usb_sel = info->muic_data->gpio_usb_sel;
+ int ret = 0;
+ int val = (path == USB_SEL_CP) ? GPIO_LEVEL_HIGH : GPIO_LEVEL_LOW;
+ int temp;
+
+#if !defined(CONFIG_TARGET_LOCALE_NA)
+ if (gpio_is_valid(gpio_usb_sel) && gpio_usb_sel) {
+ dev_info(info->dev, "%s(%d)\n", __func__, val);
+ gpio_direction_output(gpio_usb_sel, val);
+
+ temp = gpio_get_value(gpio_usb_sel);
+ if (val != temp) {
+ dev_err(info->dev, "%s(%d)=>%d\n", __func__, val, temp);
+ ret = -EAGAIN;
+ }
+ }
+#endif /* !CONFIG_TARGET_LOCALE_NA */
+
+ return ret;
+}
+
+static int set_uart_sel(struct max8997_muic_info *info, int path)
+{
+ int gpio_uart_sel = info->muic_data->gpio_uart_sel;
+ int ret = 0;
+ int val = (path == UART_PATH_AP) ? GPIO_LEVEL_HIGH : GPIO_LEVEL_LOW;
+ int temp;
+
+#if !defined(CONFIG_TARGET_LOCALE_NA)
+ if (gpio_is_valid(gpio_uart_sel) && gpio_uart_sel) {
+ dev_info(info->dev, "%s(%d)\n", __func__, val);
+ gpio_direction_output(gpio_uart_sel, val);
+
+ temp = gpio_get_value(gpio_uart_sel);
+ if (val != temp) {
+ dev_err(info->dev, "%s(%d)=>%d\n", __func__, val, temp);
+ ret = -EAGAIN;
+ }
+ }
+#endif /* !CONFIG_TARGET_LOCALE_NA */
+
+ return ret;
+}
+
+static int set_mic(struct max8997_muic_info *info, bool micen)
+{
+ struct i2c_client *client = info->muic;
+ int ret = 0;
+ u8 cntl1_val = micen << MICEN_SHIFT;
+ u8 temp = 0;
+
+ /* MIC */
+ dev_info(info->dev, "%s(%s) cntl1(0x%02x)\n", __func__,
+ (micen ? "en" : "dis"), cntl1_val);
+ ret = max8997_update_reg(client, MAX8997_MUIC_REG_CTRL1, cntl1_val,
+ MICEN_MASK);
+ if (ret < 0) {
+ dev_err(info->dev, "%s: update reg err\n", __func__);
+ return ret;
+ }
+
+ ret = max8997_read_reg(client, MAX8997_MUIC_REG_CTRL1, &temp);
+ if (ret < 0) {
+ dev_err(info->dev, "%s: read reg err\n", __func__);
+ return ret;
+ }
+
+ if ((temp & MICEN_MASK) ^ cntl1_val) {
+ dev_err(info->dev, "%s: err CNTL1(0x%02x)\n", __func__, temp);
+ ret = -EAGAIN;
+ }
+
+ return ret;
+}
+
+static int set_accdet(struct max8997_muic_info *info, u8 accdet)
{
struct i2c_client *client = info->muic;
+ int ret = 0;
+ u8 cntl2_val = accdet << CTRL2_ACCDET_SHIFT;
+ u8 temp = 0;
+
+ /* accdet */
+ dev_info(info->dev, "%s(%s) cntl2(0x%02x)\n", __func__,
+ (accdet ? "en" : "dis"), cntl2_val);
+ ret = max8997_update_reg(client, MAX8997_MUIC_REG_CTRL2, cntl2_val,
+ CTRL2_ACCDET_MASK);
+ if (ret < 0) {
+ dev_err(info->dev, "%s: update reg err\n", __func__);
+ return ret;
+ }
+
+ ret = max8997_read_reg(client, MAX8997_MUIC_REG_CTRL2, &temp);
+ if (ret < 0) {
+ dev_err(info->dev, "%s: read reg err\n", __func__);
+ return ret;
+ }
+
+ if ((temp & CTRL2_ACCDET_MASK) ^ cntl2_val) {
+ dev_err(info->dev, "%s: err CNTL2(0x%02x)\n", __func__, temp);
+ ret = -EAGAIN;
+ }
+
+ return ret;
+}
+
+static int set_com_sw(struct max8997_muic_info *info, u8 cntl1_val)
+{
+ struct i2c_client *client = info->muic;
+ int ret = 0;
+ u8 cntl1_msk = COMN1SW_MASK | COMP2SW_MASK;
+ u8 temp = 0;
+
+ /* com sw */
+ dev_info(info->dev, "%s(0x%02x)\n", __func__, cntl1_val);
+ ret = max8997_update_reg(client, MAX8997_MUIC_REG_CTRL1, cntl1_val,
+ cntl1_msk);
+ if (ret < 0) {
+ dev_err(info->dev, "%s: update reg err\n", __func__);
+ return ret;
+ }
+
+ ret = max8997_read_reg(client, MAX8997_MUIC_REG_CTRL1, &temp);
+ if (ret < 0) {
+ dev_err(info->dev, "%s: read reg err\n", __func__);
+ return ret;
+ }
+
+ if ((temp & (COMN1SW_MASK | COMP2SW_MASK)) ^ cntl1_val) {
+ dev_err(info->dev, "%s: err CNTL1(0x%02x)\n", __func__, temp);
+ ret = -EAGAIN;
+ }
+
+ return ret;
+}
+
+static int com_to_open(struct max8997_muic_info *info)
+{
+ int ret = 0;
+ u8 cntl1_val;
+ u8 temp = 0;
+
+ /* Disable Factory Accessory Detection State Machine for manual open*/
+ ret = set_accdet(info, 0x00);
+ if (ret) {
+ dev_err(info->dev, "%s: set_accdet err\n", __func__);
+ return ret;
+ }
+
+ /* com to open */
+ cntl1_val = (CTRL1_TO_OPEN << COMN1SW_SHIFT) |
+ (CTRL1_TO_OPEN << COMP2SW_SHIFT);
+ ret = set_com_sw(info, cntl1_val);
+ while (ret) {
+ ret = max8997_read_reg(info->muic, MAX8997_MUIC_REG_CTRL1,
+ &temp);
+ if (ret < 0) {
+ dev_err(info->dev, "%s: read reg err\n", __func__);
+ return ret;
+ }
+
+ if ((temp & (COMN1SW_MASK | COMP2SW_MASK)) ^ cntl1_val) {
+ dev_err(info->dev, "%s: err CNTL1(0x%02x)\n", __func__,
+ temp);
+ ret = -EAGAIN;
+ }
+#if 0
+ if (ret) {
+ dev_err(info->dev, "%s: set_com_sw err\n", __func__);
+ return ret;
+ }
+#endif
+ }
+
+ return ret;
+}
+
+static int com_to_usb(struct max8997_muic_info *info)
+{
+ int ret = 0;
+ u8 accdet = 0x01;
+ u8 cntl1_val;
+
+ if (info->acc_type == MUIC_ACC_TYPE_OTG) {
+ accdet = 0x00;
+ /* com to usb */
+ cntl1_val = (CTRL1_TO_USB << COMN1SW_SHIFT) |
+ (CTRL1_TO_USB << COMP2SW_SHIFT);
+ }
+
+ /* Enable/Disable Factory Accessory Detection State Machine */
+ ret = set_accdet(info, accdet);
+ if (ret) {
+ dev_err(info->dev, "%s: set_accdet err\n", __func__);
+ return ret;
+ }
+
+ if (!accdet) {
+ ret = set_com_sw(info, cntl1_val);
+ if (ret) {
+ dev_err(info->dev, "%s: set_com_sw err\n", __func__);
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+static int com_to_uart(struct max8997_muic_info *info)
+{
+ int ret = 0;
+ u8 cntl1_val;
+
+ /* Disable Factory Accessory Detection State Machine for manual open*/
+ ret = set_accdet(info, 0x00);
+ if (ret) {
+ dev_err(info->dev, "%s: set_accdet err\n", __func__);
+ return ret;
+ }
+
+ /* com to uart */
+ cntl1_val = (CTRL1_TO_UART << COMN1SW_SHIFT) |
+ (CTRL1_TO_UART << COMP2SW_SHIFT);
+ ret = set_com_sw(info, cntl1_val);
+ if (ret) {
+ dev_err(info->dev, "%s: set_com_sw err\n", __func__);
+ return ret;
+ }
+
+ return ret;
+}
+
+static int com_to_audio(struct max8997_muic_info *info)
+{
+ int ret = 0;
+ u8 cntl1_val;
+
+ /* Disable Factory Accessory Detection State Machine for manual open*/
+ ret = set_accdet(info, 0x00);
+ if (ret) {
+ dev_err(info->dev, "%s: set_accdet err\n", __func__);
+ return ret;
+ }
+
+ /* com to audio */
+ cntl1_val = (CTRL1_TO_AUDIO << COMN1SW_SHIFT) |
+ (CTRL1_TO_AUDIO << COMP2SW_SHIFT);
+ ret = set_com_sw(info, cntl1_val);
+ if (ret) {
+ dev_err(info->dev, "%s: set_com_sw err\n", __func__);
+ return ret;
+ }
+
+ return ret;
+}
+
+static int switch_to_ap_usb(struct max8997_muic_info *info)
+{
struct max8997_muic_data *mdata = info->muic_data;
- int ret;
- int gpio_val;
- u8 accdet, cntl1_val, cntl1_msk = 0, cntl2_val;
+ int ret = 0;
if (mdata->set_safeout) {
- ret = mdata->set_safeout(path);
+ ret = mdata->set_safeout(AP_USB_MODE);
if (ret) {
- dev_err(info->dev, "%s: fail to set safout!\n",
- __func__);
+ dev_err(info->dev, "%s: safout set err\n", __func__);
return ret;
}
}
- switch (path) {
- case AP_USB_MODE:
- dev_info(info->dev, "%s: AP_USB_MODE\n", __func__);
- gpio_val = 0;
- accdet = 1;
-
- if (info->cable_type == CABLE_TYPE_OTG) {
- accdet = 0;
- /* DN1, DP2 */
- cntl1_val = (1 << COMN1SW_SHIFT) | (1 << COMP2SW_SHIFT);
- cntl1_msk = COMN1SW_MASK | COMP2SW_MASK;
+ dev_info(info->dev, "%s\n", __func__);
+
+ ret = com_to_usb(info);
+ if (ret) {
+ dev_err(info->dev, "%s: com->usb set err\n", __func__);
+ return ret;
+ }
+
+ return ret;
+}
+
+static int switch_to_ap_uart(struct max8997_muic_info *info)
+{
+ int ret = 0;
+
+ dev_info(info->dev, "%s\n", __func__);
+
+ ret = set_uart_sel(info, UART_PATH_AP);
+ if (ret) {
+ dev_err(info->dev, "%s: uart sel set err\n", __func__);
+ return ret;
+ }
+
+ ret = set_usb_sel(info, USB_SEL_IF);
+ if (ret) {
+ dev_err(info->dev, "%s: usb sel set err\n", __func__);
+ return ret;
+ }
+
+ ret = com_to_uart(info);
+ if (ret) {
+ dev_err(info->dev, "%s: com->uart set err\n", __func__);
+ return ret;
+ }
+
+ return ret;
+}
+
+static int switch_to_cp_usb(struct max8997_muic_info *info)
+{
+ struct max8997_muic_data *mdata = info->muic_data;
+ int ret = 0;
+
+ if (mdata->set_safeout) {
+ ret = mdata->set_safeout(CP_USB_MODE);
+ if (ret) {
+ dev_err(info->dev, "%s: safout set err\n", __func__);
+ return ret;
}
+ }
+
+ dev_info(info->dev, "%s\n", __func__);
+
+ ret = set_usb_sel(info, USB_SEL_CP);
+ if (ret) {
+ dev_err(info->dev, "%s: usb sel set err\n", __func__);
+ return ret;
+ }
+
+ ret = com_to_uart(info);
+ if (ret) {
+ dev_err(info->dev, "%s: com->uart set err\n", __func__);
+ return ret;
+ }
+
+ return ret;
+}
+
+static int switch_to_cp_uart(struct max8997_muic_info *info)
+{
+ int ret = 0;
+
+ dev_info(info->dev, "%s\n", __func__);
+
+ ret = set_uart_sel(info, UART_PATH_CP);
+ if (ret) {
+ dev_err(info->dev, "%s: uart sel set err\n", __func__);
+ return ret;
+ }
+
+ ret = set_usb_sel(info, USB_SEL_IF);
+ if (ret) {
+ dev_err(info->dev, "%s: usb sel set err\n", __func__);
+ return ret;
+ }
+
+ ret = com_to_uart(info);
+ if (ret) {
+ dev_err(info->dev, "%s: com->uart set err\n", __func__);
+ return ret;
+ }
+
+ return ret;
+}
+
+static int switch_to_audio(struct max8997_muic_info *info)
+{
+ int ret = 0;
+
+ dev_info(info->dev, "%s\n", __func__);
+
+ ret = set_mic(info, false);
+ if (ret) {
+ dev_err(info->dev, "%s: mic en set err\n", __func__);
+ return ret;
+ }
+
+ ret = com_to_audio(info);
+ if (ret) {
+ dev_err(info->dev, "%s: com->audio set err\n", __func__);
+ return ret;
+ }
+
+ return ret;
+}
+
+static enum cable_type chg_type_convertor(enum muic_chg_type chg_type)
+{
+ enum cable_type charger_type;
+ switch (chg_type) {
+ case MUIC_CHG_TYPE_USB:
+ charger_type = CABLE_TYPE_USB;
break;
- case CP_USB_MODE:
- dev_info(info->dev, "%s: CP_USB_MODE\n", __func__);
- gpio_val = 1;
- accdet = 0;
- /* UT1, UR2 */
- cntl1_val = (3 << COMN1SW_SHIFT) | (3 << COMP2SW_SHIFT);
- cntl1_msk = COMN1SW_MASK | COMP2SW_MASK;
+ case MUIC_CHG_TYPE_TA:
+ charger_type = CABLE_TYPE_TA;
break;
- case AUDIO_MODE:
- dev_info(info->dev, "%s: AUDIO_MODE\n", __func__);
- gpio_val = 0;
- accdet = 0;
- /* SL1, SR2 */
- cntl1_val = (2 << COMN1SW_SHIFT) | (2 << COMP2SW_SHIFT) |
- (0 << MICEN_SHIFT);
- cntl1_msk = COMN1SW_MASK | COMP2SW_MASK | MICEN_MASK;
+ case MUIC_CHG_TYPE_MHL_VB:
+ charger_type = CABLE_TYPE_MHL_VB;
break;
+ case MUIC_CHG_TYPE_NONE:
+ case MUIC_CHG_TYPE_UNKNOWN:
default:
- dev_warn(info->dev, "%s: invalid path(%d)\n", __func__, path);
- return -EINVAL;
+ charger_type = CABLE_TYPE_NONE;
+ break;
}
-#if !defined(CONFIG_TARGET_LOCALE_NA) && !defined(CONFIG_MACH_U1CAMERA_BD)
- if (gpio_is_valid(info->muic_data->gpio_usb_sel))
- gpio_direction_output(mdata->gpio_usb_sel, gpio_val);
-#endif /* !CONFIG_TARGET_LOCALE_NA && !CONFIG_MACH_U1CAMERA_BD */
- /* Enable/Disable Factory Accessory Detection State Machine */
- cntl2_val = accdet << CTRL2_ACCDET_SHIFT;
- max8997_update_reg(client, MAX8997_MUIC_REG_CTRL2, cntl2_val,
- CTRL2_ACCDET_MASK);
+ return charger_type;
+}
- if (!accdet) {
- dev_info(info->dev, "%s: Set manual path\n", __func__);
- max8997_update_reg(client, MAX8997_MUIC_REG_CTRL1, cntl1_val,
- cntl1_msk);
+static int attach_charger(struct max8997_muic_info *info,
+ enum muic_chg_type chg_type)
+{
+ struct max8997_muic_data *mdata = info->muic_data;
+ int ret = 0;
- cntl1_val = 0;
- max8997_read_reg(client, MAX8997_MUIC_REG_CTRL1, &cntl1_val);
- dev_info(info->dev, "%s: CNTL1(0x%02x)\n", __func__, cntl1_val);
+ dev_info(info->dev, "%s: chg_type(0x%x)\n", __func__, chg_type);
+
+ if (!mdata->charger_cb) {
+ dev_err(info->dev, "%s: charger_cb is NULL\n", __func__);
+ return -ENXIO;
}
- return 0;
+ if (info->chg_type == chg_type) {
+ dev_info(info->dev, "%s: already, ignore.\n", __func__);
+ return ret;
+ }
+
+ ret = mdata->charger_cb(chg_type_convertor(chg_type));
+ if (ret) {
+ info->chg_type = MUIC_CHG_TYPE_NONE;
+ dev_err(info->dev, "%s: charger_cb(%d) err\n", __func__, ret);
+ return ret;
+ }
+
+ info->chg_type = chg_type;
+
+ return ret;
}
-static int max8997_muic_set_charging_type(struct max8997_muic_info *info,
- bool force_disable)
+static int attach_usb_util(struct max8997_muic_info *info)
+{
+ int ret = 0;
+
+ ret = attach_charger(info, MUIC_CHG_TYPE_USB);
+ if (ret)
+ return ret;
+
+ if (info->muic_data->usb_path == CP_USB_MODE) {
+ ret = switch_to_cp_usb(info);
+ return ret;
+ }
+
+ ret = switch_to_ap_usb(info);
+ return ret;
+}
+
+static int detach_charger(struct max8997_muic_info *info)
{
struct max8997_muic_data *mdata = info->muic_data;
int ret = 0;
- if (mdata->charger_cb) {
- if (force_disable)
- ret = mdata->charger_cb(CABLE_TYPE_NONE);
- else
- ret = mdata->charger_cb(info->cable_type);
+ dev_info(info->dev, "%s\n", __func__);
+
+ if (!mdata->charger_cb) {
+ dev_err(info->dev, "%s: charger_cb is NULL\n", __func__);
+ return -ENXIO;
}
+ ret = mdata->charger_cb(chg_type_convertor(MUIC_CHG_TYPE_NONE));
if (ret) {
- dev_err(info->dev, "%s: error from charger_cb(%d)\n", __func__,
- ret);
+ dev_err(info->dev, "%s: charger_cb(%d) err\n", __func__, ret);
return ret;
}
- return 0;
+ info->chg_type = MUIC_CHG_TYPE_NONE;
+
+ return ret;
+}
+
+static int attach_jig_usb_boot_off(struct max8997_muic_info *info, u8 vbvolt)
+{
+ int ret = 0;
+
+ if (!(vbvolt & STATUS2_VBVOLT_MASK))
+ return ret;
+
+ if (info->acc_type == MUIC_ACC_TYPE_JIG_USB_OFF) {
+ dev_info(info->dev, "%s: duplicated(JIG USB OFF)\n", __func__);
+ return ret;
+ }
+
+ dev_info(info->dev, "%s\n", __func__);
+ info->acc_type = MUIC_ACC_TYPE_JIG_USB_OFF;
+
+ ret = attach_usb_util(info);
+ return ret;
+}
+
+static int attach_jig_usb_boot_on(struct max8997_muic_info *info, u8 vbvolt)
+{
+ int ret = 0;
+
+ if (!(vbvolt & STATUS2_VBVOLT_MASK))
+ return ret;
+
+ if (info->acc_type == MUIC_ACC_TYPE_JIG_USB_ON) {
+ dev_info(info->dev, "%s: duplicated(JIG USB ON)\n", __func__);
+ return ret;
+ }
+
+ dev_info(info->dev, "%s\n", __func__);
+ info->acc_type = MUIC_ACC_TYPE_JIG_USB_ON;
+
+ ret = attach_usb_util(info);
+ return ret;
+}
+
+static int attach_usb(struct max8997_muic_info *info)
+{
+ struct max8997_muic_data *mdata = info->muic_data;
+ int ret = 0;
+
+ if (info->acc_type == MUIC_ACC_TYPE_USB) {
+ dev_info(info->dev, "%s: duplicated(USB)\n", __func__);
+ return ret;
+ }
+
+ dev_info(info->dev, "%s\n", __func__);
+ info->acc_type = MUIC_ACC_TYPE_USB;
+
+ ret = attach_usb_util(info);
+ if (ret)
+ return ret;
+
+ if (mdata->usb_cb && info->is_usb_ready)
+ mdata->usb_cb(USB_CABLE_ATTACHED);
+
+ return ret;
+}
+
+static int detach_usb(struct max8997_muic_info *info)
+{
+ struct max8997_muic_data *mdata = info->muic_data;
+ int ret = 0;
+
+ dev_info(info->dev, "%s: acc_type(0x%x)\n", __func__, info->acc_type);
+
+ if (info->acc_type != MUIC_ACC_TYPE_MHL) {
+ ret = detach_charger(info);
+ if (ret)
+ return ret;
+
+ ret = com_to_open(info);
+ if (ret)
+ return ret;
+
+ info->acc_type = MUIC_ACC_TYPE_NONE;
+ }
+
+ if (mdata->usb_path == CP_USB_MODE)
+ return ret;
+
+ if (mdata->usb_cb && info->is_usb_ready)
+ mdata->usb_cb(USB_CABLE_DETACHED);
+
+ return ret;
+}
+
+static int attach_mhl(struct max8997_muic_info *info, u8 chgtyp)
+{
+ struct max8997_muic_data *mdata = info->muic_data;
+ int ret = 0;
+
+ if (mdata->is_mhl_attached && mdata->is_mhl_attached()) {
+ dev_info(info->dev, "%s: mhl_sel HIGH, do nothing\n", __func__);
+ return ret;
+ }
+
+ dev_info(info->dev, "%s\n", __func__);
+
+ info->acc_type = MUIC_ACC_TYPE_MHL;
+
+ if (info->chg_type == MUIC_CHG_TYPE_USB) {
+ dev_info(info->dev, "%s: chg_type USB, detach usb\n", __func__);
+ ret = detach_usb(info);
+ }
+
+#ifdef CONFIG_EXTCON
+ if (info->edev && info->is_mhl_ready)
+ extcon_set_cable_state(info->edev, "MHL", true);
+#else
+ if (mdata->mhl_cb && info->is_mhl_ready)
+ mdata->mhl_cb(MAX8997_MUIC_ATTACHED);
+#endif /* CONFIG_EXTCON */
+
+ if (chgtyp == CHGTYP_USB) {
+ dev_info(info->dev, "%s: chgtyp USB, attach TA\n", __func__);
+ ret = attach_charger(info, MUIC_CHG_TYPE_MHL_VB);
+ }
+
+ return ret;
+}
+
+static int detach_mhl(struct max8997_muic_info *info)
+{
+ struct max8997_muic_data *mdata = info->muic_data;
+ int ret = 0;
+
+ dev_info(info->dev, "%s%s\n", __func__,
+ info->chg_type == MUIC_CHG_TYPE_MHL_VB ? " VBUS" : "");
+
+ if (info->chg_type != MUIC_CHG_TYPE_MHL_VB) {
+ if (mdata->is_mhl_attached && mdata->is_mhl_attached())
+ dev_info(info->dev, "%s: MHL attached, Do Nothing\n",
+ __func__);
+ else
+ info->acc_type = MUIC_ACC_TYPE_NONE;
+
+ return ret;
+ }
+
+ ret = detach_charger(info);
+ if (ret)
+ dev_err(info->dev, "%s: detach_charger err\n", __func__);
+
+ if (mdata->is_mhl_attached && mdata->is_mhl_attached() &&
+ mdata->mhl_cb && info->is_mhl_ready)
+ mdata->mhl_cb(MAX8997_MUIC_DETACHED);
+
+ info->acc_type = MUIC_ACC_TYPE_NONE;
+
+ return ret;
+}
+
+static int attach_otg(struct max8997_muic_info *info, u8 chgtyp)
+{
+ struct max8997_muic_data *mdata = info->muic_data;
+ int ret = 0;
+
+ switch (chgtyp) {
+ case CHGTYP_NO_VOLTAGE:
+ if (info->acc_type == MUIC_ACC_TYPE_OTG) {
+ dev_info(info->dev, "%s: duplicated(OTG)\n", __func__);
+ return ret;
+ }
+
+ info->acc_type = MUIC_ACC_TYPE_OTG;
+ ret = switch_to_ap_usb(info);
+ if (mdata->usb_cb && info->is_usb_ready)
+ mdata->usb_cb(USB_OTGHOST_ATTACHED);
+ break;
+ case CHGTYP_USB:
+ case CHGTYP_DOWNSTREAM_PORT:
+ case CHGTYP_DEDICATED_CHGR:
+ case CHGTYP_500MA:
+ case CHGTYP_1A:
+ dev_info(info->dev, "%s: OTG charging pump\n", __func__);
+ ret = attach_charger(info, MUIC_CHG_TYPE_NONE);
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+static int detach_otg(struct max8997_muic_info *info)
+{
+ struct max8997_muic_data *mdata = info->muic_data;
+ int ret = 0;
+
+ dev_info(info->dev, "%s\n", __func__);
+ info->acc_type = MUIC_ACC_TYPE_NONE;
+
+ if (mdata->usb_cb && info->is_usb_ready)
+ mdata->usb_cb(USB_OTGHOST_DETACHED);
+
+ return ret;
}
-static int max8997_muic_handle_dock_vol_key(struct max8997_muic_info *info,
+/* TODO : should be removed */
+#define NOTIFY_TEST_MODE 3
+
+static int attach_jig_uart_boot_off(struct max8997_muic_info *info, u8 vbvolt)
+{
+ struct max8997_muic_data *mdata = info->muic_data;
+ bool is_otgtest = false;
+ int ret = 0;
+
+ dev_info(info->dev, "%s: JIG UART/BOOTOFF(0x%x)\n", __func__, vbvolt);
+
+#if defined(CONFIG_SEC_MODEM_M0_TD)
+ gpio_set_value(GPIO_AP_CP_INT1, 1);
+#endif
+
+ if (mdata->uart_path == UART_PATH_AP)
+ ret = switch_to_ap_uart(info);
+ else
+ ret = switch_to_cp_uart(info);
+
+ info->acc_type = MUIC_ACC_TYPE_JIG_UART_OFF;
+
+ if (vbvolt & STATUS2_VBVOLT_MASK) {
+ if (mdata->host_notify_cb) {
+ if (mdata->host_notify_cb(1) == NOTIFY_TEST_MODE) {
+ is_otgtest = true;
+ dev_info(info->dev, "%s: OTG TEST\n", __func__);
+ }
+ }
+
+ if (is_otgtest)
+ ret = detach_charger(info);
+ else
+ ret = attach_charger(info, MUIC_CHG_TYPE_TA);
+ } else {
+ if (info->chg_type == MUIC_CHG_TYPE_TA) {
+ ret = detach_charger(info);
+
+ if (mdata->host_notify_cb)
+ mdata->host_notify_cb(0);
+ }
+ }
+
+ return ret;
+}
+
+static int detach_jig_uart_boot_off(struct max8997_muic_info *info)
+{
+ int ret = 0;
+
+ dev_info(info->dev, "%s\n", __func__);
+ info->acc_type = MUIC_ACC_TYPE_NONE;
+
+ if (info->chg_type == MUIC_CHG_TYPE_TA)
+ ret = detach_charger(info);
+
+ return ret;
+}
+
+static int detach_jig_uart_boot_on(struct max8997_muic_info *info)
+{
+ int ret = 0;
+
+ dev_info(info->dev, "%s\n", __func__);
+ info->acc_type = MUIC_ACC_TYPE_NONE;
+
+ if (info->chg_type == MUIC_CHG_TYPE_TA)
+ ret = detach_charger(info);
+
+ return ret;
+}
+
+static int attach_desk_dock(struct max8997_muic_info *info, u8 status2)
+{
+ struct max8997_muic_data *mdata = info->muic_data;
+ u8 chgtyp, chgdetrun;
+ int ret = 0;
+
+ chgtyp = status2 & STATUS2_CHGTYP_MASK;
+ chgdetrun = status2 & STATUS2_CHGDETRUN_MASK;
+
+ if (info->acc_type == MUIC_ACC_TYPE_DESKDOCK)
+ dev_info(info->dev, "%s: duplicated(DeskDock)\n", __func__);
+ else {
+ dev_info(info->dev, "%s\n", __func__);
+ info->acc_type = MUIC_ACC_TYPE_DESKDOCK;
+
+ ret = switch_to_audio(info);
+
+ if (mdata->deskdock_cb)
+ mdata->deskdock_cb(MAX8997_MUIC_ATTACHED);
+ }
+
+ switch (chgtyp) {
+ case CHGTYP_USB:
+ case CHGTYP_DOWNSTREAM_PORT:
+ case CHGTYP_DEDICATED_CHGR:
+ case CHGTYP_500MA:
+ case CHGTYP_1A:
+ ret = attach_charger(info, MUIC_CHG_TYPE_TA);
+ break;
+ case CHGTYP_NO_VOLTAGE:
+ if ((info->chg_type == MUIC_CHG_TYPE_TA) && (!chgdetrun))
+ ret = detach_charger(info);
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+static int detach_desk_dock(struct max8997_muic_info *info)
+{
+ struct max8997_muic_data *mdata = info->muic_data;
+ int ret = 0;
+
+ dev_info(info->dev, "%s\n", __func__);
+
+ if (info->chg_type == MUIC_CHG_TYPE_TA) {
+ ret = detach_charger(info);
+ if (ret)
+ return ret;
+ }
+
+ if (mdata->deskdock_cb)
+ mdata->deskdock_cb(MAX8997_MUIC_DETACHED);
+
+ info->acc_type = MUIC_ACC_TYPE_NONE;
+
+ return ret;
+}
+
+static int attach_car_dock(struct max8997_muic_info *info, u8 status2)
+{
+ struct max8997_muic_data *mdata = info->muic_data;
+ u8 chgtyp, chgdetrun;
+ int ret = 0;
+
+ chgtyp = status2 & STATUS2_CHGTYP_MASK;
+ chgdetrun = status2 & STATUS2_CHGDETRUN_MASK;
+
+ if (info->acc_type == MUIC_ACC_TYPE_CARDOCK)
+ dev_info(info->dev, "%s: duplicated(CarDock)\n", __func__);
+ else {
+ dev_info(info->dev, "%s\n", __func__);
+ info->acc_type = MUIC_ACC_TYPE_CARDOCK;
+
+ ret = switch_to_audio(info);
+
+ if (mdata->cardock_cb)
+ mdata->cardock_cb(MAX8997_MUIC_ATTACHED);
+ }
+
+ switch (chgtyp) {
+ case CHGTYP_USB:
+ case CHGTYP_DOWNSTREAM_PORT:
+ case CHGTYP_DEDICATED_CHGR:
+ case CHGTYP_500MA:
+ case CHGTYP_1A:
+ ret = attach_charger(info, MUIC_CHG_TYPE_TA);
+ break;
+ case CHGTYP_NO_VOLTAGE:
+ if ((info->chg_type == MUIC_CHG_TYPE_TA) && (!chgdetrun))
+ ret = detach_charger(info);
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+static int detach_car_dock(struct max8997_muic_info *info)
+{
+ struct max8997_muic_data *mdata = info->muic_data;
+ int ret = 0;
+
+ dev_info(info->dev, "%s\n", __func__);
+
+ if (info->chg_type == MUIC_CHG_TYPE_TA) {
+ ret = detach_charger(info);
+ if (ret)
+ return ret;
+ }
+
+ if (mdata->cardock_cb)
+ mdata->cardock_cb(MAX8997_MUIC_DETACHED);
+
+ info->acc_type = MUIC_ACC_TYPE_NONE;
+
+ return ret;
+}
+
+static int handle_dock_vol_key(struct max8997_muic_info *info,
u8 status1)
{
struct input_dev *input = info->input;
@@ -659,7 +1410,7 @@ static int max8997_muic_handle_dock_vol_key(struct max8997_muic_info *info,
adc = status1 & STATUS1_ADC_MASK;
- if (info->cable_type != CABLE_TYPE_DESKDOCK)
+ if (info->acc_type != MUIC_ACC_TYPE_DESKDOCK)
return 0;
if (adc == ADC_OPEN) {
@@ -774,203 +1525,7 @@ static int max8997_muic_handle_dock_vol_key(struct max8997_muic_info *info,
return 1;
}
-static int max8997_muic_attach_usb_type(struct max8997_muic_info *info, int adc)
-{
- struct max8997_muic_data *mdata = info->muic_data;
- int ret, path;
-
- if (info->cable_type == CABLE_TYPE_MHL ||
- info->cable_type == CABLE_TYPE_MHL_VB) {
- dev_warn(info->dev, "%s: mHL was attached!\n", __func__);
- return 0;
- }
-
- switch (adc) {
- case ADC_JIG_USB_OFF:
- if (info->cable_type == CABLE_TYPE_JIG_USB_OFF) {
- dev_info(info->dev, "%s: duplicated(JIG USB OFF)\n",
- __func__);
- return 0;
- }
-
- dev_info(info->dev, "%s:JIG USB BOOTOFF\n", __func__);
- info->cable_type = CABLE_TYPE_JIG_USB_OFF;
- path = AP_USB_MODE;
- break;
- case ADC_JIG_USB_ON:
- if (info->cable_type == CABLE_TYPE_JIG_USB_ON) {
- dev_info(info->dev, "%s: duplicated(JIG USB ON)\n",
- __func__);
- return 0;
- }
-
- dev_info(info->dev, "%s:JIG USB BOOTON\n", __func__);
- info->cable_type = CABLE_TYPE_JIG_USB_ON;
- path = AP_USB_MODE;
- break;
- case ADC_OPEN:
- if (info->cable_type == CABLE_TYPE_USB) {
- dev_info(info->dev, "%s: duplicated(USB)\n", __func__);
- return 0;
- }
-
- dev_info(info->dev, "%s:USB\n", __func__);
- info->cable_type = CABLE_TYPE_USB;
- path = AP_USB_MODE;
- break;
- default:
- dev_info(info->dev, "%s: Unkown cable(0x%x)\n", __func__, adc);
- return 0;
- }
-
- ret = max8997_muic_set_charging_type(info, false);
- if (ret) {
- info->cable_type = CABLE_TYPE_NONE;
- return ret;
- }
-
- if (mdata->sw_path == CP_USB_MODE) {
- info->cable_type = CABLE_TYPE_USB;
- max8997_muic_set_usb_path(info, CP_USB_MODE);
- return 0;
- }
-
- max8997_muic_set_usb_path(info, path);
-
- if ((path == AP_USB_MODE) && (adc == ADC_OPEN)) {
- if (mdata->usb_cb && info->is_usb_ready)
- mdata->usb_cb(USB_CABLE_ATTACHED);
- }
-
- return 0;
-}
-
-static int max8997_muic_attach_dock_type(struct max8997_muic_info *info,
- int adc)
-{
- struct max8997_muic_data *mdata = info->muic_data;
- int path;
-
- switch (adc) {
- case ADC_DESKDOCK:
- /* Desk Dock */
- if (info->cable_type == CABLE_TYPE_DESKDOCK) {
- dev_info(info->dev, "%s: duplicated(DeskDock)\n",
- __func__);
- return 0;
- }
- dev_info(info->dev, "%s:DeskDock\n", __func__);
- info->cable_type = CABLE_TYPE_DESKDOCK;
- path = AUDIO_MODE;
-
- if (mdata->deskdock_cb)
- mdata->deskdock_cb(MAX8997_MUIC_ATTACHED);
- break;
- case ADC_CARDOCK:
- /* Car Dock */
- if (info->cable_type == CABLE_TYPE_CARDOCK) {
- dev_info(info->dev, "%s: duplicated(CarDock)\n",
- __func__);
- return 0;
- }
- dev_info(info->dev, "%s:CarDock\n", __func__);
- info->cable_type = CABLE_TYPE_CARDOCK;
- path = AUDIO_MODE;
-
- if (mdata->cardock_cb)
- mdata->cardock_cb(MAX8997_MUIC_ATTACHED);
- break;
- default:
- dev_info(info->dev, "%s: should not reach here(0x%x)\n",
- __func__, adc);
- return 0;
- }
-
- max8997_muic_set_usb_path(info, path);
-
- return 0;
-}
-
-static void max8997_muic_attach_mhl(struct max8997_muic_info *info, u8 chgtyp)
-{
- struct max8997_muic_data *mdata = info->muic_data;
-
- dev_info(info->dev, "%s\n", __func__);
-
- if (info->cable_type == CABLE_TYPE_USB) {
- if (mdata->usb_cb && info->is_usb_ready)
- mdata->usb_cb(USB_CABLE_DETACHED);
-
- max8997_muic_set_charging_type(info, true);
- }
-#if 0
- if (info->cable_type == CABLE_TYPE_MHL) {
- dev_info(info->dev, "%s: duplicated(MHL)\n", __func__);
- return;
- }
-#endif
- info->cable_type = CABLE_TYPE_MHL;
-
- if (mdata->mhl_cb && info->is_mhl_ready)
- mdata->mhl_cb(MAX8997_MUIC_ATTACHED);
-
- if (chgtyp == CHGTYP_USB) {
- info->cable_type = CABLE_TYPE_MHL_VB;
- max8997_muic_set_charging_type(info, false);
- }
-}
-
-/* TODO : should be removed */
-#define NOTIFY_TEST_MODE 3
-
-static void max8997_muic_handle_jig_uart(struct max8997_muic_info *info,
- u8 vbvolt)
-{
- struct max8997_muic_data *mdata = info->muic_data;
- enum cable_type prev_ct = info->cable_type;
- bool is_otgtest = false;
- u8 cntl1_val, cntl1_msk;
-
- dev_info(info->dev, "%s: JIG UART/BOOTOFF(0x%x)\n", __func__, vbvolt);
-
-#if defined(CONFIG_SEC_MODEM_M0_TD)
- gpio_set_value(GPIO_AP_CP_INT1, 1);
-#endif
-
- /* UT1, UR2 */
- cntl1_val = (3 << COMN1SW_SHIFT) | (3 << COMP2SW_SHIFT);
- cntl1_msk = COMN1SW_MASK | COMP2SW_MASK;
- max8997_update_reg(info->muic, MAX8997_MUIC_REG_CTRL1, cntl1_val,
- cntl1_msk);
-
- if (vbvolt & STATUS2_VBVOLT_MASK) {
- if (mdata->host_notify_cb) {
- if (mdata->host_notify_cb(1) == NOTIFY_TEST_MODE) {
- is_otgtest = true;
- dev_info(info->dev, "%s: OTG TEST\n", __func__);
- }
- }
-
- info->cable_type = CABLE_TYPE_JIG_UART_OFF_VB;
- max8997_muic_set_charging_type(info, is_otgtest);
-
- } else {
- info->cable_type = CABLE_TYPE_JIG_UART_OFF;
-#if 0
- if (mdata->uart_path == UART_PATH_CP &&
- mdata->jig_uart_cb)
- mdata->jig_uart_cb(UART_PATH_CP);
-#endif
- if (prev_ct == CABLE_TYPE_JIG_UART_OFF_VB) {
- max8997_muic_set_charging_type(info, false);
-
- if (mdata->host_notify_cb)
- mdata->host_notify_cb(0);
- }
- }
-}
-
-static int max8997_muic_handle_attach(struct max8997_muic_info *info,
+static int handle_attach(struct max8997_muic_info *info,
u8 status1, u8 status2)
{
struct max8997_muic_data *mdata = info->muic_data;
@@ -988,9 +1543,8 @@ static int max8997_muic_handle_attach(struct max8997_muic_info *info,
vbvolt = status2 & STATUS2_VBVOLT_MASK;
chgdetrun = status2 & STATUS2_CHGDETRUN_MASK;
- switch (info->cable_type) {
- case CABLE_TYPE_JIG_UART_OFF:
- case CABLE_TYPE_JIG_UART_OFF_VB:
+ switch (info->acc_type) {
+ case MUIC_ACC_TYPE_JIG_UART_OFF:
/* Workaround for Factory mode.
* Abandon adc interrupt of approximately +-100K range
* if previous cable status was JIG UART BOOT OFF.
@@ -998,62 +1552,51 @@ static int max8997_muic_handle_attach(struct max8997_muic_info *info,
if (adc == (ADC_JIG_UART_OFF + 1) ||
adc == (ADC_JIG_UART_OFF - 1)) {
dev_warn(info->dev, "%s: abandon ADC\n", __func__);
- return 0;
+ return ret;
}
if (adcerr) {
- dev_warn(info->dev, "%s: current state is jig_uart_off,"
- "just ignore\n", __func__);
- return 0;
+ dev_warn(info->dev, "%s: current jig_uart_off, ignore\n",
+ __func__);
+ return ret;
}
if (adc != ADC_JIG_UART_OFF) {
- if (info->cable_type == CABLE_TYPE_JIG_UART_OFF_VB) {
- dev_info(info->dev, "%s: adc != JIG_UART_OFF, remove JIG UART/OFF/VB\n", __func__);
- info->cable_type = CABLE_TYPE_NONE;
- max8997_muic_set_charging_type(info, false);
- } else {
- dev_info(info->dev, "%s: adc != JIG_UART_OFF, remove JIG UART/BOOTOFF\n", __func__);
- info->cable_type = CABLE_TYPE_NONE;
- }
+ dev_info(info->dev, "%s: adc != JIG_UART_OFF,"
+ " remove JIG UART/BOOTOFF\n", __func__);
+ ret = detach_jig_uart_boot_off(info);
}
break;
- case CABLE_TYPE_DESKDOCK:
+ case MUIC_ACC_TYPE_DESKDOCK:
if (adcerr || (adc != ADC_DESKDOCK)) {
if (adcerr)
- dev_err(info->dev, "%s: ADC err occured(DESKDOCK)\n", __func__);
+ dev_err(info->dev, "%s: ADC err occured(DESKDOCK)\n",
+ __func__);
else
- dev_warn(info->dev, "%s: ADC != DESKDOCK, remove DESKDOCK\n", __func__);
-
- info->cable_type = CABLE_TYPE_NONE;
-
- max8997_muic_set_charging_type(info, false);
+ dev_warn(info->dev, "%s: ADC != DESKDOCK, remove DESKDOCK\n",
+ __func__);
- if (mdata->deskdock_cb)
- mdata->deskdock_cb(MAX8997_MUIC_DETACHED);
+ ret = detach_desk_dock(info);
if (adcerr)
- return 0;
+ return ret;
}
break;
- case CABLE_TYPE_CARDOCK:
+ case MUIC_ACC_TYPE_CARDOCK:
if (adcerr || (adc != ADC_CARDOCK)) {
if (adcerr)
- dev_err(info->dev, "%s: ADC err occured(CARDOCK)\n", __func__);
+ dev_err(info->dev, "%s: ADC err occured(CARDOCK)\n",
+ __func__);
else
- dev_warn(info->dev, "%s: ADC != CARDOCK, remove CARDOCK\n", __func__);
-
- info->cable_type = CABLE_TYPE_NONE;
-
- max8997_muic_set_charging_type(info, false);
+ dev_warn(info->dev, "%s: ADC != CARDOCK, remove CARDOCK\n",
+ __func__);
- if (mdata->cardock_cb)
- mdata->cardock_cb(MAX8997_MUIC_DETACHED);
+ ret = detach_car_dock(info);
if (adcerr)
- return 0;
+ return ret;
}
break;
@@ -1066,119 +1609,102 @@ static int max8997_muic_handle_attach(struct max8997_muic_info *info,
* New MUIC : ADC value is not set(Open), ADCLow:1, ADCError:1
*/
if (adclow && adcerr) {
- max8997_muic_attach_mhl(info, chgtyp);
- return 0;
+ ret = attach_mhl(info, chgtyp);
+ return ret;
}
switch (adc) {
case ADC_GND:
-#if defined(CONFIG_MACH_U1)
+#if defined(CONFIG_MACH_U1) || defined(CONFIG_MACH_TRATS)
/* This is for support old MUIC */
if (adclow) {
- max8997_muic_attach_mhl(info, chgtyp);
+ ret = attach_mhl(info, chgtyp);
break;
}
-#endif
-
- if (chgtyp == CHGTYP_NO_VOLTAGE) {
- if (info->cable_type == CABLE_TYPE_OTG) {
- dev_info(info->dev,
- "%s: duplicated(OTG)\n",
- __func__);
- break;
- }
-
- info->cable_type = CABLE_TYPE_OTG;
- max8997_muic_set_usb_path(info, AP_USB_MODE);
- if (mdata->usb_cb && info->is_usb_ready)
- mdata->usb_cb(USB_OTGHOST_ATTACHED);
- } else if (chgtyp == CHGTYP_USB ||
- chgtyp == CHGTYP_DOWNSTREAM_PORT ||
- chgtyp == CHGTYP_DEDICATED_CHGR ||
- chgtyp == CHGTYP_500MA ||
- chgtyp == CHGTYP_1A) {
- dev_info(info->dev, "%s: OTG charging pump\n",
- __func__);
- ret = max8997_muic_set_charging_type(info, false);
- }
+#endif /* CONFIG_MACH_U1 || CONFIG_MACH_TRATS */
+ ret = attach_otg(info, chgtyp);
break;
case ADC_MHL:
-#if defined(CONFIG_MACH_U1)
+#if defined(CONFIG_MACH_U1) || defined(CONFIG_MACH_TRATS)
/* This is for support old MUIC */
- max8997_muic_attach_mhl(info, chgtyp);
-#endif
+ ret = attach_mhl(info, chgtyp);
+#endif /* CONFIG_MACH_U1 || CONFIG_MACH_TRATS */
break;
case ADC_JIG_UART_OFF:
- max8997_muic_handle_jig_uart(info, vbvolt);
+ ret = attach_jig_uart_boot_off(info, vbvolt);
break;
case ADC_JIG_USB_OFF:
+ ret = attach_jig_usb_boot_off(info, vbvolt);
+ break;
case ADC_JIG_USB_ON:
- if (vbvolt & STATUS2_VBVOLT_MASK)
- ret = max8997_muic_attach_usb_type(info, adc);
+ ret = attach_jig_usb_boot_on(info, vbvolt);
break;
case ADC_DESKDOCK:
+ ret = attach_desk_dock(info, status2);
+ break;
case ADC_CARDOCK:
- max8997_muic_attach_dock_type(info, adc);
- if (chgtyp == CHGTYP_USB ||
- chgtyp == CHGTYP_DOWNSTREAM_PORT ||
- chgtyp == CHGTYP_DEDICATED_CHGR ||
- chgtyp == CHGTYP_500MA ||
- chgtyp == CHGTYP_1A)
- ret = max8997_muic_set_charging_type(info, false);
- else if (chgtyp == CHGTYP_NO_VOLTAGE && !chgdetrun)
- ret = max8997_muic_set_charging_type(info, true);
+ ret = attach_car_dock(info, status2);
break;
case ADC_CEA936ATYPE1_CHG:
case ADC_CEA936ATYPE2_CHG:
case ADC_OPEN:
switch (chgtyp) {
case CHGTYP_USB:
- if (adc == ADC_CEA936ATYPE1_CHG
- || adc == ADC_CEA936ATYPE2_CHG)
+ if (adc == ADC_CEA936ATYPE1_CHG ||
+ adc == ADC_CEA936ATYPE2_CHG)
break;
- if (mdata->is_mhl_attached
- && mdata->is_mhl_attached() &&
- info->cable_type == CABLE_TYPE_MHL) {
+
+ if (mdata->is_mhl_attached &&
+ mdata->is_mhl_attached() &&
+ info->acc_type == MUIC_ACC_TYPE_MHL) {
+ if (info->chg_type == MUIC_CHG_TYPE_MHL_VB) {
+ dev_info(info->dev,
+ "%s: MHL charging already, do nothing\n",
+ __func__);
+ return ret;
+ }
+
dev_info(info->dev, "%s: MHL(charging)\n",
__func__);
- info->cable_type = CABLE_TYPE_MHL_VB;
- ret = max8997_muic_set_charging_type(info,
- false);
+ ret = attach_charger(info,
+ MUIC_CHG_TYPE_MHL_VB);
return ret;
}
- ret = max8997_muic_attach_usb_type(info, adc);
+ ret = attach_usb(info);
break;
case CHGTYP_DOWNSTREAM_PORT:
case CHGTYP_DEDICATED_CHGR:
case CHGTYP_500MA:
case CHGTYP_1A:
+ if (info->acc_type == MUIC_ACC_TYPE_TA &&
+ info->chg_type == MUIC_CHG_TYPE_TA) {
+ dev_info(info->dev, "%s: duplicated(TA)\n",
+ __func__);
+ return ret;
+ }
dev_info(info->dev, "%s:TA\n", __func__);
- info->cable_type = CABLE_TYPE_TA;
#ifdef CONFIG_USBHUB_USB3803
/* setting usb hub in default mode (standby) */
usb3803_set_mode(USB_3803_MODE_STANDBY);
-#endif /* CONFIG_USBHUB_USB3803 */
- ret = max8997_muic_set_charging_type(info, false);
- if (ret)
- info->cable_type = CABLE_TYPE_NONE;
+#endif /* CONFIG_USBHUB_USB3803 */
+ ret = attach_charger(info, MUIC_CHG_TYPE_TA);
+ if (ret == 0)
+ info->acc_type = MUIC_ACC_TYPE_TA;
break;
default:
break;
}
break;
default:
- dev_warn(info->dev, "%s: unsupported adc=0x%x\n", __func__,
- adc);
+ dev_warn(info->dev, "%s: unsupported adc=0x%x\n",
+ __func__, adc);
break;
}
return ret;
}
-static int max8997_muic_handle_detach(struct max8997_muic_info *info)
+static int handle_detach(struct max8997_muic_info *info)
{
- struct i2c_client *client = info->muic;
- struct max8997_muic_data *mdata = info->muic_data;
- enum cable_type prev_ct = CABLE_TYPE_NONE;
int ret = 0;
#if defined(CONFIG_SEC_MODEM_M0_TD)
@@ -1193,13 +1719,13 @@ static int max8997_muic_handle_detach(struct max8997_muic_info *info)
* D+/D- is short) if charger(TA) insertion is followed right after the
* JIG off. Reset CONTROL1 is needed when detaching cable.
*/
- max8997_write_reg(client, MAX8997_MUIC_REG_CTRL1, 0x00);
-
- if (info->cable_type == CABLE_TYPE_MHL) {
+ ret = max8997_write_reg(info->muic, MAX8997_MUIC_REG_CTRL1, 0x00);
+ if (ret)
+ dev_err(info->dev, "%s: reset CONTROL1 err\n", __func__);
+ if (info->acc_type == MUIC_ACC_TYPE_MHL) {
/* Enable Factory Accessory Detection State Machine */
- max8997_update_reg(client, MAX8997_MUIC_REG_CTRL2,
- (1 << CTRL2_ACCDET_SHIFT), CTRL2_ACCDET_MASK);
+ set_accdet(info, 0x01);
}
#ifdef CONFIG_USBHUB_USB3803
@@ -1208,163 +1734,100 @@ static int max8997_muic_handle_detach(struct max8997_muic_info *info)
#endif /* CONFIG_USBHUB_USB3803 */
info->previous_key = DOCK_KEY_NONE;
- if (info->cable_type == CABLE_TYPE_NONE) {
- dev_info(info->dev, "%s: duplicated(NONE)\n", __func__);
- return 0;
- }
-#if 0
- if (mdata->jig_uart_cb)
- mdata->jig_uart_cb(UART_PATH_AP);
-#endif
- if (mdata->is_mhl_attached && mdata->is_mhl_attached()
- && info->cable_type == CABLE_TYPE_MHL) {
- dev_info(info->dev, "%s: MHL attached. Do Nothing\n",
- __func__);
- return 0;
- }
-
- switch (info->cable_type) {
- case CABLE_TYPE_OTG:
- dev_info(info->dev, "%s: OTG\n", __func__);
- info->cable_type = CABLE_TYPE_NONE;
-
- if (mdata->usb_cb && info->is_usb_ready)
- mdata->usb_cb(USB_OTGHOST_DETACHED);
+ switch (info->acc_type) {
+ case MUIC_ACC_TYPE_OTG:
+ ret = detach_otg(info);
break;
- case CABLE_TYPE_USB:
- case CABLE_TYPE_JIG_USB_OFF:
- case CABLE_TYPE_JIG_USB_ON:
- dev_info(info->dev, "%s: USB(0x%x)\n", __func__,
- info->cable_type);
- prev_ct = info->cable_type;
- info->cable_type = CABLE_TYPE_NONE;
-
- ret = max8997_muic_set_charging_type(info, false);
- if (ret) {
- info->cable_type = prev_ct;
- break;
- }
-
- if (mdata->sw_path == CP_USB_MODE)
- return 0;
-
- if (mdata->usb_cb && info->is_usb_ready)
- mdata->usb_cb(USB_CABLE_DETACHED);
+ case MUIC_ACC_TYPE_USB:
+ case MUIC_ACC_TYPE_JIG_USB_OFF:
+ case MUIC_ACC_TYPE_JIG_USB_ON:
+ ret = detach_usb(info);
break;
- case CABLE_TYPE_DESKDOCK:
- dev_info(info->dev, "%s: DESKDOCK\n", __func__);
- info->cable_type = CABLE_TYPE_NONE;
-
- ret = max8997_muic_set_charging_type(info, false);
- if (ret) {
- info->cable_type = CABLE_TYPE_DESKDOCK;
- break;
- }
-
- if (mdata->deskdock_cb)
- mdata->deskdock_cb(MAX8997_MUIC_DETACHED);
+ case MUIC_ACC_TYPE_DESKDOCK:
+ ret = detach_desk_dock(info);
break;
- case CABLE_TYPE_CARDOCK:
- dev_info(info->dev, "%s: CARDOCK\n", __func__);
- info->cable_type = CABLE_TYPE_NONE;
-
- ret = max8997_muic_set_charging_type(info, false);
- if (ret) {
- info->cable_type = CABLE_TYPE_CARDOCK;
- break;
- }
-
- if (mdata->cardock_cb)
- mdata->cardock_cb(MAX8997_MUIC_DETACHED);
+ case MUIC_ACC_TYPE_CARDOCK:
+ ret = detach_car_dock(info);
break;
- case CABLE_TYPE_TA:
- dev_info(info->dev, "%s: TA\n", __func__);
- info->cable_type = CABLE_TYPE_NONE;
- ret = max8997_muic_set_charging_type(info, false);
- if (ret)
- info->cable_type = CABLE_TYPE_TA;
- break;
- case CABLE_TYPE_JIG_UART_ON:
- dev_info(info->dev, "%s: JIG UART/BOOTON\n", __func__);
- info->cable_type = CABLE_TYPE_NONE;
+ case MUIC_ACC_TYPE_TA:
+ ret = detach_charger(info);
+ info->acc_type = MUIC_ACC_TYPE_NONE;
break;
- case CABLE_TYPE_JIG_UART_OFF:
- dev_info(info->dev, "%s: JIG UART/BOOTOFF\n", __func__);
- info->cable_type = CABLE_TYPE_NONE;
+ case MUIC_ACC_TYPE_JIG_UART_ON:
+ ret = detach_jig_uart_boot_on(info);
break;
- case CABLE_TYPE_JIG_UART_OFF_VB:
- dev_info(info->dev, "%s: JIG UART/OFF/VB\n", __func__);
- info->cable_type = CABLE_TYPE_NONE;
- ret = max8997_muic_set_charging_type(info, false);
- if (ret)
- info->cable_type = CABLE_TYPE_JIG_UART_OFF_VB;
+ case MUIC_ACC_TYPE_JIG_UART_OFF:
+ ret = detach_jig_uart_boot_off(info);
break;
- case CABLE_TYPE_MHL:
- dev_info(info->dev, "%s: MHL\n", __func__);
- info->cable_type = CABLE_TYPE_NONE;
+ case MUIC_ACC_TYPE_MHL:
+ ret = detach_mhl(info);
+#ifdef CONFIG_EXTCON
+ if (info->edev && info->is_mhl_ready)
+ extcon_set_cable_state(info->edev, "MHL", false);
+#endif
break;
- case CABLE_TYPE_MHL_VB:
- dev_info(info->dev, "%s: MHL VBUS\n", __func__);
- info->cable_type = CABLE_TYPE_NONE;
- max8997_muic_set_charging_type(info, false);
-
- if (mdata->is_mhl_attached && mdata->is_mhl_attached()) {
- if (mdata->mhl_cb && info->is_mhl_ready)
- mdata->mhl_cb(MAX8997_MUIC_DETACHED);
- }
+ case MUIC_ACC_TYPE_NONE:
+ dev_info(info->dev, "%s: duplicated(NONE)\n", __func__);
break;
- case CABLE_TYPE_UNKNOWN:
+ case MUIC_ACC_TYPE_UNKNOWN:
dev_info(info->dev, "%s: UNKNOWN\n", __func__);
- info->cable_type = CABLE_TYPE_NONE;
-
- ret = max8997_muic_set_charging_type(info, false);
- if (ret)
- info->cable_type = CABLE_TYPE_UNKNOWN;
+ ret = detach_charger(info);
+ info->acc_type = MUIC_ACC_TYPE_NONE;
break;
default:
dev_info(info->dev, "%s:invalid cable type %d\n",
- __func__, info->cable_type);
+ __func__, info->acc_type);
break;
}
+
return ret;
}
-static void max8997_muic_detect_dev(struct max8997_muic_info *info, int irq)
+static int detect_dev(struct max8997_muic_info *info, int irq)
{
struct i2c_client *client = info->muic;
u8 status[2];
- u8 adc, chgtyp, adcerr;
+ u8 adc, chgtyp, adcerr, chgdetrun;
int intr = INT_ATTACH;
- int ret;
+ int ret = 0;
ret = max8997_bulk_read(client, MAX8997_MUIC_REG_STATUS1, 2, status);
if (ret) {
- dev_err(info->dev, "%s: fail to read muic reg(%d)\n", __func__,
+ dev_err(info->dev, "%s: read muic reg(%d) err\n", __func__,
ret);
- return;
+ return ret;
}
dev_info(info->dev, "%s: STATUS1:0x%x, 2:0x%x\n", __func__,
status[0], status[1]);
- if ((irq == info->irq_adc) &&
- max8997_muic_handle_dock_vol_key(info, status[0]))
- return;
+ if (irq == info->irq_adc) {
+ ret = handle_dock_vol_key(info, status[0]);
+ if (ret)
+ return ret;
+ }
adc = status[0] & STATUS1_ADC_MASK;
adcerr = status[0] & STATUS1_ADCERR_MASK;
chgtyp = status[1] & STATUS2_CHGTYP_MASK;
+ chgdetrun = status[1] & STATUS2_CHGDETRUN_MASK;
+
+ if (chgdetrun) {
+ dev_warn(info->dev, "%s: charger det run, ignore\n", __func__);
+ return ret;
+ }
switch (adc) {
case ADC_MHL:
-#if defined(CONFIG_MACH_U1)
+#if defined(CONFIG_MACH_U1) || defined(CONFIG_MACH_TRATS)
break;
-#endif
+#endif /* CONFIG_MACH_U1 || CONFIG_MACH_TRATS */
case (ADC_MHL + 1):
case (ADC_DOCK_VOL_DN - 1):
case (ADC_DOCK_PLAY_PAUSE_KEY + 2) ... (ADC_CEA936ATYPE1_CHG - 1):
case (ADC_CARDOCK + 1):
- dev_warn(info->dev, "%s: unsupported ADC(0x%02x)\n", __func__, adc);
+ dev_warn(info->dev, "%s: unsupported ADC(0x%02x)\n",
+ __func__, adc);
intr = INT_DETACH;
break;
case ADC_OPEN:
@@ -1376,10 +1839,22 @@ static void max8997_muic_detect_dev(struct max8997_muic_info *info, int irq)
chgtyp == CHGTYP_DEDICATED_CHGR ||
chgtyp == CHGTYP_500MA ||
chgtyp == CHGTYP_1A) {
- if (info->cable_type == CABLE_TYPE_OTG ||
- info->cable_type == CABLE_TYPE_DESKDOCK ||
- info->cable_type == CABLE_TYPE_CARDOCK)
+ switch (info->acc_type) {
+ case MUIC_ACC_TYPE_OTG:
+ case MUIC_ACC_TYPE_STATION:
+ case MUIC_ACC_TYPE_JIG_USB_OFF:
+ case MUIC_ACC_TYPE_JIG_USB_ON:
+ case MUIC_ACC_TYPE_DESKDOCK:
+ case MUIC_ACC_TYPE_JIG_UART_OFF:
+ case MUIC_ACC_TYPE_JIG_UART_ON:
+ case MUIC_ACC_TYPE_CARDOCK:
+ dev_info(info->dev, "%s: chgtyp accur but detach\n",
+ __func__);
intr = INT_DETACH;
+ break;
+ default:
+ break;
+ }
}
}
break;
@@ -1391,20 +1866,20 @@ static void max8997_muic_detect_dev(struct max8997_muic_info *info, int irq)
if (intr == INT_ATTACH) {
if (irq == info->irq_chgins) {
if (info->is_ovp_state) {
- max8997_muic_handle_attach(info, status[0],
+ ret = handle_attach(info, status[0],
status[1]);
info->is_ovp_state = false;
dev_info(info->dev, "OVP recovered\n");
- return;
+ return ret;
} else {
dev_info(info->dev, "Just inserted TA/USB\n");
- return;
+ return ret;
}
} else if (irq == info->irq_chgrm) {
- max8997_muic_handle_detach(info);
+ ret = handle_detach(info);
info->is_ovp_state = true;
dev_info(info->dev, "OVP occured\n");
- return;
+ return ret;
}
} else {
@@ -1412,28 +1887,32 @@ static void max8997_muic_detect_dev(struct max8997_muic_info *info, int irq)
if (irq == info->irq_chgrm) {
dev_info(info->dev, "Just removed TA/USB\n");
- return;
+ return ret;
}
}
-#endif
+#endif /* CONFIG_MUIC_MAX8997_OVPUI */
if (intr == INT_ATTACH) {
dev_info(info->dev, "%s: ATTACHED\n", __func__);
- max8997_muic_handle_attach(info, status[0], status[1]);
+ ret = handle_attach(info, status[0], status[1]);
} else {
dev_info(info->dev, "%s: DETACHED\n", __func__);
- max8997_muic_handle_detach(info);
+ ret = handle_detach(info);
}
- return;
+ return ret;
}
static irqreturn_t max8997_muic_irq(int irq, void *data)
{
struct max8997_muic_info *info = data;
+ int ret = 0;
+
dev_info(info->dev, "%s: irq:%d\n", __func__, irq);
mutex_lock(&info->mutex);
- max8997_muic_detect_dev(info, irq);
+ ret = detect_dev(info, irq);
+ if (ret)
+ dev_err(info->dev, "%s: error returned.\n", __func__);
mutex_unlock(&info->mutex);
return IRQ_HANDLED;
@@ -1451,19 +1930,6 @@ do { \
static int max8997_muic_irq_init(struct max8997_muic_info *info)
{
int ret;
-#if 0
-#if !defined(CONFIG_MACH_U1_REV00)
- dev_info(info->dev, "%s: system_rev=%d\n", __func__, system_rev);
-#if !defined(CONFIG_MACH_P6_REV02) && !defined(CONFIG_MACH_U1_C210) \
- && !defined(CONFIG_MACH_U1HD_C210)
- if (system_rev < 0x3) {
- dev_info(info->dev,
- "Caution !!! This system_rev does not support ALL irq\n");
- return 0;
- }
-#endif
-#endif
-#endif
REQUEST_IRQ(info->irq_adc, "muic-adc");
REQUEST_IRQ(info->irq_chgtype, "muic-chgtype");
@@ -1472,7 +1938,7 @@ static int max8997_muic_irq_init(struct max8997_muic_info *info)
#if defined(CONFIG_MUIC_MAX8997_OVPUI)
REQUEST_IRQ(info->irq_chgins, "chg-insert");
REQUEST_IRQ(info->irq_chgrm, "chg-remove");
-#endif
+#endif /* CONFIG_MUIC_MAX8997_OVPUI */
return 0;
}
@@ -1496,7 +1962,7 @@ static void max8997_muic_init_detect(struct work_struct *work)
return;
mutex_lock(&info->mutex);
- max8997_muic_detect_dev(info, -1);
+ detect_dev(info, -1);
mutex_unlock(&info->mutex);
}
@@ -1513,15 +1979,19 @@ static void max8997_muic_usb_detect(struct work_struct *work)
mutex_lock(&info->mutex);
info->is_usb_ready = true;
- if (info->muic_data->sw_path != CP_USB_MODE) {
+ if (info->muic_data->usb_path != CP_USB_MODE) {
if (mdata->usb_cb) {
- switch (info->cable_type) {
- case CABLE_TYPE_USB:
- case CABLE_TYPE_JIG_USB_OFF:
- case CABLE_TYPE_JIG_USB_ON:
+ switch (info->acc_type) {
+ case MUIC_ACC_TYPE_USB:
+ case MUIC_ACC_TYPE_JIG_USB_OFF:
+ case MUIC_ACC_TYPE_JIG_USB_ON:
+ dev_info(info->dev, "%s: usb attach\n",
+ __func__);
mdata->usb_cb(USB_CABLE_ATTACHED);
break;
- case CABLE_TYPE_OTG:
+ case MUIC_ACC_TYPE_OTG:
+ dev_info(info->dev, "%s: otg attach\n",
+ __func__);
mdata->usb_cb(USB_OTGHOST_ATTACHED);
break;
default:
@@ -1544,20 +2014,26 @@ static void max8997_muic_mhl_detect(struct work_struct *work)
mutex_lock(&info->mutex);
info->is_mhl_ready = true;
-#ifndef CONFIG_MACH_U1
+#if !defined(CONFIG_MACH_U1) && !defined(CONFIG_MACH_TRATS)
if (mdata->is_mhl_attached) {
if (!mdata->is_mhl_attached())
goto out;
}
-#endif
- if (info->cable_type == CABLE_TYPE_MHL || \
- info->cable_type == CABLE_TYPE_MHL_VB) {
+#endif /* !CONFIG_MACH_U1 */
+ if (info->acc_type == MUIC_ACC_TYPE_MHL) {
+ dev_info(info->dev, "%s: mhl attach\n", __func__);
+#ifdef CONFIG_EXTCON
+ if (info->edev)
+ extcon_set_cable_state(info->edev, "MHL", true);
+#else
if (mdata->mhl_cb)
mdata->mhl_cb(MAX8997_MUIC_ATTACHED);
+#endif
}
out:
mutex_unlock(&info->mutex);
}
+
extern struct device *switch_dev;
static int __devinit max8997_muic_probe(struct platform_device *pdev)
@@ -1587,13 +2063,13 @@ static int __devinit max8997_muic_probe(struct platform_device *pdev)
#if defined(CONFIG_MUIC_MAX8997_OVPUI)
info->irq_chgins = max8997->irq_base + MAX8997_IRQ_CHGINS;
info->irq_chgrm = max8997->irq_base + MAX8997_IRQ_CHGRM;
-#endif
+#endif /* CONFIG_MUIC_MAX8997_OVPUI */
if (pdata->muic) {
info->muic_data = pdata->muic;
- info->muic_data->sw_path = AP_USB_MODE;
+ info->muic_data->usb_path = AP_USB_MODE;
}
- info->cable_type = CABLE_TYPE_UNKNOWN;
-
+ info->acc_type = MUIC_ACC_TYPE_UNKNOWN;
+ info->chg_type = MUIC_CHG_TYPE_UNKNOWN;
platform_set_drvdata(pdev, info);
@@ -1621,8 +2097,8 @@ static int __devinit max8997_muic_probe(struct platform_device *pdev)
"error: %d\n", __func__, ret);
goto err_input;
}
+#if !defined(CONFIG_MACH_U1_NA_USCC)
-#if !defined(CONFIG_MACH_U1CAMERA_BD)
if (info->muic_data && gpio_is_valid(info->muic_data->gpio_usb_sel)) {
CHECK_GPIO(info->muic_data->gpio_usb_sel, "USB_SEL");
@@ -1630,7 +2106,7 @@ static int __devinit max8997_muic_probe(struct platform_device *pdev)
info->muic_data->uart_path =
info->muic_data->cfg_uart_gpio();
-#ifndef CONFIG_TARGET_LOCALE_NA
+#if !defined(CONFIG_TARGET_LOCALE_NA)
ret = gpio_request(info->muic_data->gpio_usb_sel, "USB_SEL");
if (ret) {
dev_info(info->dev, "%s: fail to request gpio(%d)\n",
@@ -1639,22 +2115,39 @@ static int __devinit max8997_muic_probe(struct platform_device *pdev)
}
if (gpio_get_value(info->muic_data->gpio_usb_sel)) {
dev_info(info->dev, "%s: CP USB\n", __func__);
- info->muic_data->sw_path = CP_USB_MODE;
+ info->muic_data->usb_path = CP_USB_MODE;
}
-#endif /* CONFIG_TARGET_LOCALE_NA */
+#endif /* !CONFIG_TARGET_LOCALE_NA */
}
-#endif /* CONFIG_MACH_U1CAMERA_BD */
-
+#endif
/* create sysfs group*/
ret = sysfs_create_group(&switch_dev->kobj, &max8997_muic_group);
dev_set_drvdata(switch_dev, info);
if (ret) {
dev_err(&pdev->dev,
- "failed to create max8997 muic attribute group\n");
+ "failed to create max8997 muic attribute group\n");
goto fail;
}
+#ifdef CONFIG_EXTCON
+ /* External connector */
+ info->edev = kzalloc(sizeof(struct extcon_dev), GFP_KERNEL);
+ if (!info->edev) {
+ pr_err("Failed to allocate memory for extcon device\n");
+ ret = -ENOMEM;
+ goto fail;
+ }
+ info->edev->name = DEV_NAME;
+ info->edev->supported_cable = extcon_cable_name;
+ ret = extcon_dev_register(info->edev, NULL);
+ if (ret) {
+ pr_err("Failed to register extcon device\n");
+ kfree(info->edev);
+ goto fail;
+ }
+#endif
+
if (info->muic_data && info->muic_data->init_cb)
info->muic_data->init_cb();
@@ -1663,9 +2156,12 @@ static int __devinit max8997_muic_probe(struct platform_device *pdev)
/* Set ADC debounce time: 25ms */
max8997_muic_set_adcdbset(info, 2);
+ mutex_lock(&info->mutex);
+
ret = max8997_muic_irq_init(info);
if (ret < 0) {
dev_err(&pdev->dev, "Failed to initialize MUIC irq:%d\n", ret);
+ mutex_unlock(&info->mutex);
goto fail;
}
@@ -1679,6 +2175,8 @@ static int __devinit max8997_muic_probe(struct platform_device *pdev)
INIT_DELAYED_WORK(&info->mhl_work, max8997_muic_mhl_detect);
schedule_delayed_work(&info->mhl_work, msecs_to_jiffies(25000));
+ mutex_unlock(&info->mutex);
+
return 0;
fail:
@@ -1714,9 +2212,9 @@ static int __devexit max8997_muic_remove(struct platform_device *pdev)
free_irq(info->irq_chgtype, info);
free_irq(info->irq_vbvolt, info);
free_irq(info->irq_adcerr, info);
-#if !defined(CONFIG_TARGET_LOCALE_NA) && !defined(CONFIG_MACH_U1CAMERA_BD)
+#if !defined(CONFIG_TARGET_LOCALE_NA)
gpio_free(info->muic_data->gpio_usb_sel);
-#endif /* CONFIG_TARGET_LOCALE_NA && CONFIG_MACH_U1CAMERA_BD */
+#endif /* !CONFIG_TARGET_LOCALE_NA */
mutex_destroy(&info->mutex);
kfree(info);
}
@@ -1730,7 +2228,7 @@ static u8 max8997_dumpaddr_muic[] = {
MAX8997_MUIC_REG_CTRL2,
};
-#ifdef CONFIG_PM
+#if defined(CONFIG_PM)
static int max8997_muic_freeze(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
@@ -1749,7 +2247,7 @@ static int max8997_muic_freeze(struct device *dev)
/* hibernation state, disconnect usb state*/
dev_info(info->dev, "%s: DETACHED\n", __func__);
mutex_lock(&info->mutex);
- max8997_muic_handle_detach(info);
+ handle_detach(info);
mutex_unlock(&info->mutex);
return 0;
@@ -1767,7 +2265,7 @@ static int max8997_muic_restore(struct device *dev)
info->max8997->reg_dump[i + MAX8997_REG_PMIC_END]);
mutex_lock(&info->mutex);
- max8997_muic_detect_dev(info, -1);
+ detect_dev(info, -1);
mutex_unlock(&info->mutex);
return 0;
@@ -1781,7 +2279,7 @@ static const struct dev_pm_ops max8997_dev_pm_ops = {
#define MAX8997_DEV_PM_OPS (&max8997_dev_pm_ops)
#else
#define MAX8997_DEV_PM_OPS NULL
-#endif
+#endif /* CONFIG_PM */
void max8997_muic_shutdown(struct device *dev)
{
diff --git a/drivers/misc/modem_if/Kconfig b/drivers/misc/modem_if/Kconfig
index 94cb48c..6a6eeab 100644
--- a/drivers/misc/modem_if/Kconfig
+++ b/drivers/misc/modem_if/Kconfig
@@ -34,6 +34,16 @@ config CDMA_MODEM_MDM6600
depends on SEC_MODEM
default n
+config TDSCDMA_MODEM_SPRD8803
+ bool "modem chip : SPRD SC8803"
+ depends on SEC_MODEM
+ default n
+
+config GSM_MODEM_ESC6270
+ bool "modem chip : QC ESC6270"
+ depends on SEC_MODEM
+ default n
+
config LINK_DEVICE_MIPI
bool "modem driver link device MIPI-HSI"
depends on SEC_MODEM
@@ -44,6 +54,10 @@ config LINK_DEVICE_DPRAM
depends on SEC_MODEM
default n
+config LINK_DEVICE_PLD
+ bool "modem driver link device PLD"
+ depends on SEC_MODEM
+ default n
config LINK_DEVICE_USB
bool "modem driver link device USB"
depends on SEC_MODEM
@@ -59,6 +73,16 @@ config LINK_DEVICE_C2C
depends on SEC_MODEM
default n
+config LINK_DEVICE_SPI
+ bool "modem driver link device SPI"
+ depends on SEC_MODEM
+ default n
+
+config WORKQUEUE_FRONT
+ bool "IPC: SPI workqueue front"
+ depends on SEC_MODEM
+ default n
+
config IPC_CMC22x_OLD_RFS
bool "IPC: CMC22x ancient RFS"
depends on SEC_MODEM
@@ -73,3 +97,8 @@ config SIM_DETECT
bool "SIM_DETECT pin"
depends on SEC_MODEM
default n
+
+config SIM_SLOT_SWITCH
+ bool "SIM_SLOT_SWITCH"
+ depends on SEC_MODEM
+ default n
diff --git a/drivers/misc/modem_if/Makefile b/drivers/misc/modem_if/Makefile
index dafc8c0..5bd62ea 100644
--- a/drivers/misc/modem_if/Makefile
+++ b/drivers/misc/modem_if/Makefile
@@ -4,7 +4,7 @@ EXTRA_CFLAGS += -Idrivers/misc/modem_if
obj-y += sipc5_modem.o sipc5_io_device.o
obj-y += sipc4_modem.o sipc4_io_device.o
-obj-y += modem_net_flowcontrol_device.o modem_utils.o modem_debug.o
+obj-y += modem_net_flowcontrol_device.o modem_utils.o
obj-$(CONFIG_UMTS_MODEM_XMM6260) += modem_modemctl_device_xmm6260.o
obj-$(CONFIG_UMTS_MODEM_XMM6262) += modem_modemctl_device_xmm6262.o
@@ -12,9 +12,15 @@ obj-$(CONFIG_CDMA_MODEM_CBP71) += modem_modemctl_device_cbp71.o
obj-$(CONFIG_CDMA_MODEM_CBP72) += modem_modemctl_device_cbp72.o
obj-$(CONFIG_LTE_MODEM_CMC221) += modem_modemctl_device_cmc221.o lte_modem_bootloader.o
obj-$(CONFIG_CDMA_MODEM_MDM6600) += modem_modemctl_device_mdm6600.o
+obj-$(CONFIG_GSM_MODEM_ESC6270) += modem_modemctl_device_esc6270.o
+obj-$(CONFIG_TDSCDMA_MODEM_SPRD8803) += modem_modemctl_device_sprd8803.o
obj-$(CONFIG_LINK_DEVICE_MIPI) += modem_link_device_mipi.o
-obj-$(CONFIG_LINK_DEVICE_DPRAM) += modem_link_device_dpram.o
+obj-$(CONFIG_LINK_DEVICE_DPRAM) += modem_link_device_dpram.o modem_link_device_dpram_ext_op.o
+obj-$(CONFIG_LINK_DEVICE_PLD) += modem_link_device_pld.o modem_link_device_pld_ext_op.o
obj-$(CONFIG_LINK_DEVICE_USB) += modem_link_device_usb.o modem_link_pm_usb.o
obj-$(CONFIG_LINK_DEVICE_HSIC) += modem_link_device_hsic.o
obj-$(CONFIG_LINK_DEVICE_C2C) += modem_link_device_c2c.o
+obj-$(CONFIG_LINK_DEVICE_SPI) += modem_link_device_spi.o
+
+obj-$(CONFIG_SIM_SLOT_SWITCH) += modem_sim_slot_switch.o
diff --git a/drivers/misc/modem_if/modem_debug.c b/drivers/misc/modem_if/modem_debug.c
deleted file mode 100644
index 1ad3073..0000000
--- a/drivers/misc/modem_if/modem_debug.c
+++ /dev/null
@@ -1,429 +0,0 @@
-/* linux/drivers/misc/modem_if/modem_debug.c
- *
- * Copyright (C) 2010 Samsung Electronics.
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/interrupt.h>
-#include <linux/platform_device.h>
-#include <linux/miscdevice.h>
-#include <linux/if_arp.h>
-
-#include <linux/uaccess.h>
-#include <linux/fs.h>
-#include <linux/io.h>
-#include <linux/wait.h>
-#include <linux/time.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <linux/mutex.h>
-#include <linux/irq.h>
-#include <linux/gpio.h>
-#include <linux/delay.h>
-#include <linux/wakelock.h>
-
-#include <linux/platform_data/modem.h>
-#include "modem_prj.h"
-#include "modem_variation.h"
-#include "modem_utils.h"
-
-static const char *hex = "0123456789abcdef";
-
-static inline void mif_irq2str(struct mif_event_buff *evtb, char *buff)
-{
- int i;
- char tb[32];
-
- if (evtb->link_type == LINKDEV_DPRAM) {
- struct dpram_irq_buff *irqb = &evtb->dpram_irqb;
-
- sprintf(tb, "{0x%04X %d 0x%04X}",
- irqb->magic, irqb->access, irqb->int2ap);
- strcat(buff, tb);
-
- for (i = 0; i < IPC_RFS; i++) {
- snprintf(tb, 32, " {%d: %u %u %u %u}", i,
- irqb->qsp[i].txq.in, irqb->qsp[i].txq.out,
- irqb->qsp[i].rxq.in, irqb->qsp[i].rxq.out);
- strcat(buff, tb);
- }
- } else {
- sprintf(tb, "link unspeicified");
- strcat(buff, tb);
- }
-}
-
-static inline void mif_dump2hex(const char *data, size_t len, char *buff)
-{
- char *src = (char *)data;
- int i;
- char tb[4];
-
- tb[3] = 0;
- for (i = 0; i < len; i++) {
- tb[0] = hex[(*src >> 4) & 0xf];
- tb[1] = hex[*src & 0xf];
- tb[2] = ' ';
- strcat(buff, tb);
- src++;
- }
-}
-
-static inline void mif_fin_str(char *buff)
-{
- char tb[4];
- sprintf(tb, "\n");
- strcat(buff, tb);
-}
-
-static void mif_log2str(struct mif_event_buff *evtb, char *buff)
-{
- struct timeval *tv;
- struct tm date;
-
- tv = &evtb->tv;
-
- time_to_tm((tv->tv_sec - sys_tz.tz_minuteswest * 60), 0, &date);
- sprintf(evtb->time, "%02d:%02d:%02d.%03ld",
- date.tm_hour, date.tm_min, date.tm_sec, (tv->tv_usec / 1000));
-
- if (evtb->evt == MIF_IRQ_EVT) {
- sprintf(buff, "%s IRQ <%s> ", evtb->time, evtb->ld);
- mif_irq2str(evtb, buff);
- mif_fin_str(buff);
- } else {
- size_t len = evtb->len < (MAX_MIF_LOG_LEN >> 3) ?
- evtb->len : (MAX_MIF_LOG_LEN >> 3);
- sprintf(buff, "%s [%d] <%s:%s> ",
- evtb->time, evtb->evt, evtb->iod, evtb->ld);
- mif_dump2hex(evtb->data, len, buff);
- mif_fin_str(buff);
- }
-}
-
-static void mif_print_logs(struct modem_ctl *mc)
-{
- struct sk_buff *skb;
- struct mif_event_buff *evtb;
- u8 *buff;
-
- buff = kmalloc(2048, GFP_ATOMIC);
- if (!buff)
- return;
-
- while (1) {
- skb = skb_dequeue(&mc->evtq);
- if (!skb)
- break;
-
- evtb = (struct mif_event_buff *)skb->data;
- memset(buff, 0, 2048);
-
- mif_log2str(evtb, buff);
- pr_info("mif: %s", buff);
-
- dev_kfree_skb_any(skb);
- }
-
- kfree(buff);
-}
-
-static void mif_save_logs(struct modem_ctl *mc)
-{
- struct file *fp = mc->log_fp;
- struct sk_buff *skb;
- struct mif_event_buff *evtb;
- int qlen = mc->evtq.qlen;
- int i;
- int ret;
- mm_segment_t old_fs;
-
- old_fs = get_fs();
- set_fs(get_ds());
-
- for (i = 0; i < qlen; i++) {
- skb = skb_dequeue(&mc->evtq);
-
-#if 0
- if (evtb->evt < mc->log_level) {
- ret = fp->f_op->write(fp, skb->data,
- MAX_MIF_EVT_BUFF_SIZE, &fp->f_pos);
- if (ret < 0) {
- mif_log2str((struct mif_event_buff *)skb->data,
- mc->buff);
- printk(KERN_ERR "%s", mc->buff);
- }
- }
-#else
- evtb = (struct mif_event_buff *)skb->data;
- if (evtb->evt < mc->log_level) {
- mif_log2str(evtb, mc->buff);
- ret = fp->f_op->write(fp, mc->buff, strlen(mc->buff),
- &fp->f_pos);
- if (ret < 0)
- printk(KERN_ERR "%s", mc->buff);
- }
-#endif
-
- dev_kfree_skb_any(skb);
- }
-
- set_fs(old_fs);
-}
-
-static void mif_evt_work(struct work_struct *work)
-{
- struct modem_ctl *mc = container_of(work, struct modem_ctl, evt_work);
- struct file *fp = mc->log_fp;
- loff_t size;
- mm_segment_t old_fs;
-
- /* use_mif_log */
-
- if (!mc->log_level || mc->fs_failed) {
- mif_print_logs(mc);
- return;
- }
-
- /* use_mif_log && log_level && !fs_failed */
-
- if (!mc->fs_ready) {
- if (mc->evtq.qlen > 1000)
- mif_print_logs(mc);
- return;
- }
-
- /* use_mif_log && log_level && !fs_failed && fs_ready */
-
- if (fp) {
- mif_save_logs(mc);
-
- old_fs = get_fs();
- set_fs(get_ds());
- size = fp->f_pos;
- set_fs(old_fs);
- if (size > MAX_MIF_LOG_FILE_SIZE) {
- mif_err("%s size %lld > %d\n", mc->log_path, size,
- MAX_MIF_LOG_FILE_SIZE);
- mif_close_log_file(mc);
- mif_open_log_file(mc);
- }
- }
-}
-
-void mif_irq_log(struct modem_ctl *mc, struct sk_buff *skb)
-{
- if (!mc || !mc->use_mif_log)
- return;
- skb_queue_tail(&mc->evtq, skb);
-}
-
-void mif_ipc_log(struct modem_ctl *mc, enum mif_event_id evt,
- struct io_device *iod, struct link_device *ld,
- u8 *data, unsigned size)
-{
- struct sk_buff *skb;
- struct mif_event_buff *evtb;
- unsigned len;
-
- if (!mc || !mc->use_mif_log)
- return;
-
- skb = alloc_skb(MAX_MIF_EVT_BUFF_SIZE, GFP_ATOMIC);
- if (!skb)
- return;
-
- evtb = (struct mif_event_buff *)skb_put(skb, MAX_MIF_EVT_BUFF_SIZE);
- memset(evtb, 0, MAX_MIF_EVT_BUFF_SIZE);
-
- do_gettimeofday(&evtb->tv);
- evtb->evt = evt;
-
- strncpy(evtb->mc, mc->name, MAX_MIF_NAME_LEN);
-
- if (iod)
- strncpy(evtb->iod, iod->name, MAX_MIF_NAME_LEN);
-
- if (ld) {
- strncpy(evtb->ld, ld->name, MAX_MIF_NAME_LEN);
- evtb->link_type = ld->link_type;
- }
-
- len = min_t(unsigned, MAX_MIF_LOG_LEN, size);
- memcpy(evtb->data, data, len);
-
- evtb->rcvd = size;
- evtb->len = len;
-
- skb_queue_tail(&mc->evtq, skb);
-}
-
-void mif_flush_logs(struct modem_ctl *mc)
-{
- if (!mc || !mc->use_mif_log)
- return;
-
- if (atomic_read(&mc->log_open))
- queue_work(mc->evt_wq, &mc->evt_work);
-}
-
-int mif_init_log(struct modem_ctl *mc)
-{
- char wq_name[32];
- char wq_suffix[32];
-
- mc->log_level = 0;
-
- atomic_set(&mc->log_open, 0);
-
- memset(wq_name, 0, sizeof(wq_name));
- memset(wq_suffix, 0, sizeof(wq_suffix));
- strncpy(wq_name, mc->name, sizeof(wq_name));
- snprintf(wq_suffix, sizeof(wq_suffix), "%s", "_evt_wq");
- strncat(wq_name, wq_suffix, sizeof(wq_suffix));
- mc->evt_wq = create_singlethread_workqueue(wq_name);
- if (!mc->evt_wq) {
- printk(KERN_ERR "<%s:%s> fail to create %s\n",
- __func__, mc->name, wq_name);
- return -EFAULT;
- }
- printk(KERN_ERR "<%s:%s> %s created\n",
- __func__, mc->name, wq_name);
-
- INIT_WORK(&mc->evt_work, mif_evt_work);
-
- skb_queue_head_init(&mc->evtq);
-
- mc->buff = kzalloc(PAGE_SIZE, GFP_KERNEL);
- if (!mc->buff) {
- printk(KERN_ERR "<%s> kzalloc fail\n", __func__);
- return -ENOMEM;
- }
-
- return 0;
-}
-
-void mif_set_log_level(struct modem_ctl *mc)
-{
- struct file *fp;
- int ret;
- mm_segment_t old_fs;
-
- mc->log_level = 0;
-
- old_fs = get_fs();
- set_fs(get_ds());
- fp = filp_open(MIF_LOG_LV_FILE, O_RDONLY, 0);
- if (IS_ERR(fp)) {
- printk(KERN_ERR "<%s:%s> %s open fail\n",
- __func__, mc->name, MIF_LOG_LV_FILE);
- } else {
- char tb;
-
- ret = fp->f_op->read(fp, &tb, 1, &fp->f_pos);
- if (ret > 0) {
- mc->log_level = tb & 0xF;
- } else {
- printk(KERN_ERR "<%s:%s> read fail (err %d)\n",
- __func__, mc->name, ret);
- }
- }
- set_fs(old_fs);
-
- if (mc->use_mif_log && !mc->log_level)
- atomic_set(&mc->log_open, 1);
-
- printk(KERN_ERR "<%s:%s> log level = %d\n",
- __func__, mc->name, mc->log_level);
-}
-
-int mif_open_log_file(struct modem_ctl *mc)
-{
- struct timeval now;
- struct tm date;
- mm_segment_t old_fs;
-
- if (!mc || !mc->use_mif_log)
- return -EINVAL;
-
- if (!mc->log_level) {
- printk(KERN_ERR "<%s:%s> IPC logger is disabled.\n",
- __func__, mc->name);
- return -EINVAL;
- }
-
- if (!mc->fs_ready) {
- printk(KERN_ERR "<%s:%s> File system is not ready.\n",
- __func__, mc->name);
- return -EINVAL;
- }
-
- if (mc->fs_failed) {
- printk(KERN_ERR "<%s:%s> Log file cannot be created.\n",
- __func__, mc->name);
- return -EINVAL;
- }
-
- do_gettimeofday(&now);
- time_to_tm((now.tv_sec - sys_tz.tz_minuteswest * 60), 0, &date);
-
- snprintf(mc->log_path, MAX_MIF_LOG_PATH_LEN,
- "%s/%s_mif_log.%ld%02d%02d.%02d%02d%02d.txt",
- MIF_LOG_DIR, mc->name,
- (1900 + date.tm_year), (1 + date.tm_mon), date.tm_mday,
- date.tm_hour, date.tm_min, date.tm_sec);
-
- old_fs = get_fs();
- set_fs(get_ds());
- mc->log_fp = filp_open(mc->log_path, O_RDWR|O_CREAT, 0666);
- set_fs(old_fs);
- if (IS_ERR(mc->log_fp)) {
- printk(KERN_ERR "<%s:%s> %s open fail\n",
- __func__, mc->name, mc->log_path);
- mc->log_fp = NULL;
- mc->fs_failed = true;
- return -ENOENT;
- }
-
- atomic_set(&mc->log_open, 1);
-
- mif_err("open %s\n", mc->log_path);
-
- return 0;
-}
-
-void mif_close_log_file(struct modem_ctl *mc)
-{
- mm_segment_t old_fs;
-
- if (!mc || !mc->use_mif_log || !mc->log_level || mc->fs_failed ||
- !mc->fs_ready || !mc->log_fp)
- return;
-
- atomic_set(&mc->log_open, 0);
-
- flush_work_sync(&mc->evt_work);
-
- mif_err("close %s\n", mc->log_path);
-
- mif_save_logs(mc);
-
- old_fs = get_fs();
- set_fs(get_ds());
- filp_close(mc->log_fp, NULL);
- set_fs(old_fs);
-
- mc->log_fp = NULL;
-}
-
diff --git a/drivers/misc/modem_if/modem_link_device_dpram.c b/drivers/misc/modem_if/modem_link_device_dpram.c
index 862de30..a650ed9 100644
--- a/drivers/misc/modem_if/modem_link_device_dpram.c
+++ b/drivers/misc/modem_if/modem_link_device_dpram.c
@@ -31,184 +31,79 @@
#include "modem_link_device_dpram.h"
#include "modem_utils.h"
-const inline char *get_dev_name(int dev)
-{
- if (dev == IPC_FMT)
- return "FMT";
- else if (dev == IPC_RAW)
- return "RAW";
- else if (dev == IPC_RFS)
- return "RFS";
- else
- return "NONE";
-}
-
-static void log_dpram_irq(struct dpram_link_device *dpld, u16 int2ap)
-{
- struct sk_buff *skb;
- struct mif_event_buff *evtb;
- struct dpram_irq_buff *irqb;
- struct link_device *ld = &dpld->ld;
-
- skb = alloc_skb(MAX_MIF_EVT_BUFF_SIZE, GFP_ATOMIC);
- if (!skb)
- return;
-
- evtb = (struct mif_event_buff *)skb_put(skb, MAX_MIF_EVT_BUFF_SIZE);
- memset(evtb, 0, MAX_MIF_EVT_BUFF_SIZE);
-
- do_gettimeofday(&evtb->tv);
- evtb->evt = MIF_IRQ_EVT;
-
- strncpy(evtb->mc, ld->mc->name, MAX_MIF_NAME_LEN);
- strncpy(evtb->ld, ld->name, MAX_MIF_NAME_LEN);
- evtb->link_type = ld->link_type;
-
- irqb = &evtb->dpram_irqb;
-
- irqb->magic = dpld->dpctl->get_magic();
- irqb->access = dpld->dpctl->get_access();
-
- irqb->qsp[IPC_FMT].txq.in = dpld->dpctl->get_tx_head(IPC_FMT);
- irqb->qsp[IPC_FMT].txq.out = dpld->dpctl->get_tx_tail(IPC_FMT);
- irqb->qsp[IPC_FMT].rxq.in = dpld->dpctl->get_rx_head(IPC_FMT);
- irqb->qsp[IPC_FMT].rxq.out = dpld->dpctl->get_rx_tail(IPC_FMT);
-
- irqb->qsp[IPC_RAW].txq.in = dpld->dpctl->get_tx_head(IPC_RAW);
- irqb->qsp[IPC_RAW].txq.out = dpld->dpctl->get_tx_tail(IPC_RAW);
- irqb->qsp[IPC_RAW].rxq.in = dpld->dpctl->get_rx_head(IPC_RAW);
- irqb->qsp[IPC_RAW].rxq.out = dpld->dpctl->get_rx_tail(IPC_RAW);
-
- irqb->int2ap = int2ap;
-
- evtb->rcvd = sizeof(struct dpram_irq_buff);
- evtb->len = sizeof(struct dpram_irq_buff);
-
- mif_irq_log(ld->mc, skb);
- mif_flush_logs(ld->mc);
-}
+/*
+** Function prototypes for basic DPRAM operations
+*/
+static inline void clear_intr(struct dpram_link_device *dpld);
+static inline u16 recv_intr(struct dpram_link_device *dpld);
+static inline void send_intr(struct dpram_link_device *dpld, u16 mask);
+
+static inline u16 get_magic(struct dpram_link_device *dpld);
+static inline void set_magic(struct dpram_link_device *dpld, u16 val);
+static inline u16 get_access(struct dpram_link_device *dpld);
+static inline void set_access(struct dpram_link_device *dpld, u16 val);
+
+static inline u32 get_tx_head(struct dpram_link_device *dpld, int id);
+static inline u32 get_tx_tail(struct dpram_link_device *dpld, int id);
+static inline void set_tx_head(struct dpram_link_device *dpld, int id, u32 in);
+static inline void set_tx_tail(struct dpram_link_device *dpld, int id, u32 out);
+static inline u8 *get_tx_buff(struct dpram_link_device *dpld, int id);
+static inline u32 get_tx_buff_size(struct dpram_link_device *dpld, int id);
+
+static inline u32 get_rx_head(struct dpram_link_device *dpld, int id);
+static inline u32 get_rx_tail(struct dpram_link_device *dpld, int id);
+static inline void set_rx_head(struct dpram_link_device *dpld, int id, u32 in);
+static inline void set_rx_tail(struct dpram_link_device *dpld, int id, u32 out);
+static inline u8 *get_rx_buff(struct dpram_link_device *dpld, int id);
+static inline u32 get_rx_buff_size(struct dpram_link_device *dpld, int id);
+
+static inline u16 get_mask_req_ack(struct dpram_link_device *dpld, int id);
+static inline u16 get_mask_res_ack(struct dpram_link_device *dpld, int id);
+static inline u16 get_mask_send(struct dpram_link_device *dpld, int id);
+
+static inline bool dpram_circ_valid(u32 size, u32 in, u32 out);
+
+static void handle_cp_crash(struct dpram_link_device *dpld);
+static int trigger_force_cp_crash(struct dpram_link_device *dpld);
+static void dpram_dump_memory(struct link_device *ld, char *buff);
-static int memcmp16_to_io(const void __iomem *to, void *from, int size)
+/*
+** Functions for debugging
+*/
+static inline void log_dpram_status(struct dpram_link_device *dpld)
{
- u16 *d = (u16 *)to;
- u16 *s = (u16 *)from;
- int count = size >> 1;
- int diff = 0;
- int i;
- u16 d1;
- u16 s1;
-
- for (i = 0; i < count; i++) {
- d1 = ioread16(d);
- s1 = *s;
- if (d1 != s1) {
- diff++;
- mif_info("ERR! [%d] d:0x%04X != s:0x%04X\n", i, d1, s1);
- }
- d++;
- s++;
- }
-
- return diff;
+ pr_info("mif: %s: {M:0x%X A:%d} {FMT TI:%u TO:%u RI:%u RO:%u} "
+ "{RAW TI:%u TO:%u RI:%u RO:%u} {INT:0x%X}\n",
+ dpld->ld.mc->name,
+ get_magic(dpld), get_access(dpld),
+ get_tx_head(dpld, IPC_FMT), get_tx_tail(dpld, IPC_FMT),
+ get_rx_head(dpld, IPC_FMT), get_rx_tail(dpld, IPC_FMT),
+ get_tx_head(dpld, IPC_RAW), get_tx_tail(dpld, IPC_RAW),
+ get_rx_head(dpld, IPC_RAW), get_rx_tail(dpld, IPC_RAW),
+ recv_intr(dpld));
}
-static int test_dpram(char *dp_name, u8 __iomem *start, u32 size)
+static void set_dpram_map(struct dpram_link_device *dpld,
+ struct mif_irq_map *map)
{
- u8 __iomem *dst;
- int i;
- u16 val;
-
- mif_info("%s: start = 0x%p, size = %d\n", dp_name, start, size);
-
- dst = start;
- for (i = 0; i < (size >> 1); i++) {
- iowrite16((i & 0xFFFF), dst);
- dst += 2;
- }
-
- dst = start;
- for (i = 0; i < (size >> 1); i++) {
- val = ioread16(dst);
- if (val != (i & 0xFFFF)) {
- mif_info("%s: ERR! dst[%d] 0x%04X != 0x%04X\n",
- dp_name, i, val, (i & 0xFFFF));
- return -EINVAL;
- }
- dst += 2;
- }
-
- dst = start;
- for (i = 0; i < (size >> 1); i++) {
- iowrite16(0x00FF, dst);
- dst += 2;
- }
-
- dst = start;
- for (i = 0; i < (size >> 1); i++) {
- val = ioread16(dst);
- if (val != 0x00FF) {
- mif_info("%s: ERR! dst[%d] 0x%04X != 0x00FF\n",
- dp_name, i, val);
- return -EINVAL;
- }
- dst += 2;
- }
-
- dst = start;
- for (i = 0; i < (size >> 1); i++) {
- iowrite16(0x0FF0, dst);
- dst += 2;
- }
-
- dst = start;
- for (i = 0; i < (size >> 1); i++) {
- val = ioread16(dst);
- if (val != 0x0FF0) {
- mif_info("%s: ERR! dst[%d] 0x%04X != 0x0FF0\n",
- dp_name, i, val);
- return -EINVAL;
- }
- dst += 2;
- }
-
- dst = start;
- for (i = 0; i < (size >> 1); i++) {
- iowrite16(0xFF00, dst);
- dst += 2;
- }
-
- dst = start;
- for (i = 0; i < (size >> 1); i++) {
- val = ioread16(dst);
- if (val != 0xFF00) {
- mif_info("%s: ERR! dst[%d] 0x%04X != 0xFF00\n",
- dp_name, i, val);
- return -EINVAL;
- }
- dst += 2;
- }
-
- dst = start;
- for (i = 0; i < (size >> 1); i++) {
- iowrite16(0, dst);
- dst += 2;
- }
-
- dst = start;
- for (i = 0; i < (size >> 1); i++) {
- val = ioread16(dst);
- if (val != 0) {
- mif_info("%s: ERR! dst[%d] 0x%04X != 0\n",
- dp_name, i, val);
- return -EINVAL;
- }
- dst += 2;
- }
-
- mif_info("%s: PASS!!!\n", dp_name);
- return 0;
+ map->magic = get_magic(dpld);
+ map->access = get_access(dpld);
+
+ map->fmt_tx_in = get_tx_head(dpld, IPC_FMT);
+ map->fmt_tx_out = get_tx_tail(dpld, IPC_FMT);
+ map->fmt_rx_in = get_rx_head(dpld, IPC_FMT);
+ map->fmt_rx_out = get_rx_tail(dpld, IPC_FMT);
+ map->raw_tx_in = get_tx_head(dpld, IPC_RAW);
+ map->raw_tx_out = get_tx_tail(dpld, IPC_RAW);
+ map->raw_rx_in = get_rx_head(dpld, IPC_RAW);
+ map->raw_rx_out = get_rx_tail(dpld, IPC_RAW);
+
+ map->cp2ap = recv_intr(dpld);
}
+/*
+** RXB (DPRAM RX buffer) functions
+*/
static struct dpram_rxb *rxbq_create_pool(unsigned size, int count)
{
struct dpram_rxb *rxb;
@@ -304,12 +199,16 @@ static inline void rxb_clear(struct dpram_rxb *rxb)
rxb->len = 0;
}
+/*
+** DPRAM operations
+*/
static int dpram_register_isr(unsigned irq, irqreturn_t (*isr)(int, void*),
- unsigned long flag, const char *name, struct link_device *ld)
+ unsigned long flag, const char *name,
+ struct dpram_link_device *dpld)
{
- int ret = 0;
+ int ret;
- ret = request_irq(irq, isr, flag, name, ld);
+ ret = request_irq(irq, isr, flag, name, dpld);
if (ret) {
mif_info("%s: ERR! request_irq fail (err %d)\n", name, ret);
return ret;
@@ -319,11 +218,311 @@ static int dpram_register_isr(unsigned irq, irqreturn_t (*isr)(int, void*),
if (ret)
mif_info("%s: ERR! enable_irq_wake fail (err %d)\n", name, ret);
- mif_info("%s: IRQ#%d handler registered\n", name, irq);
+ mif_info("%s (#%d) handler registered\n", name, irq);
return 0;
}
+static inline void clear_intr(struct dpram_link_device *dpld)
+{
+ if (likely(dpld->dpctl->clear_intr))
+ dpld->dpctl->clear_intr();
+}
+
+static inline u16 recv_intr(struct dpram_link_device *dpld)
+{
+ if (likely(dpld->dpctl->recv_intr))
+ return dpld->dpctl->recv_intr();
+ else
+ return ioread16(dpld->mbx2ap);
+}
+
+static inline void send_intr(struct dpram_link_device *dpld, u16 mask)
+{
+ if (likely(dpld->dpctl->send_intr))
+ dpld->dpctl->send_intr(mask);
+ else
+ iowrite16(mask, dpld->mbx2cp);
+}
+
+static inline u16 get_magic(struct dpram_link_device *dpld)
+{
+ return ioread16(dpld->magic);
+}
+
+static inline void set_magic(struct dpram_link_device *dpld, u16 val)
+{
+ iowrite16(val, dpld->magic);
+}
+
+static inline u16 get_access(struct dpram_link_device *dpld)
+{
+ return ioread16(dpld->access);
+}
+
+static inline void set_access(struct dpram_link_device *dpld, u16 val)
+{
+ iowrite16(val, dpld->access);
+}
+
+static inline u32 get_tx_head(struct dpram_link_device *dpld, int id)
+{
+ return ioread16(dpld->dev[id]->txq.head);
+}
+
+static inline u32 get_tx_tail(struct dpram_link_device *dpld, int id)
+{
+ return ioread16(dpld->dev[id]->txq.tail);
+}
+
+static inline void set_tx_head(struct dpram_link_device *dpld, int id, u32 in)
+{
+ int cnt = 3;
+ u32 val = 0;
+
+ iowrite16((u16)in, dpld->dev[id]->txq.head);
+
+ do {
+ /* Check head value written */
+ val = ioread16(dpld->dev[id]->txq.head);
+ if (likely(val == in))
+ return;
+
+ mif_err("ERR: %s txq.head(%d) != in(%d)\n",
+ get_dev_name(id), val, in);
+ udelay(100);
+
+ /* Write head value again */
+ iowrite16((u16)in, dpld->dev[id]->txq.head);
+ } while (cnt--);
+
+ trigger_force_cp_crash(dpld);
+}
+
+static inline void set_tx_tail(struct dpram_link_device *dpld, int id, u32 out)
+{
+ int cnt = 3;
+ u32 val = 0;
+
+ iowrite16((u16)out, dpld->dev[id]->txq.tail);
+
+ do {
+ /* Check tail value written */
+ val = ioread16(dpld->dev[id]->txq.tail);
+ if (likely(val == out))
+ return;
+
+ mif_err("ERR: %s txq.tail(%d) != out(%d)\n",
+ get_dev_name(id), val, out);
+ udelay(100);
+
+ /* Write tail value again */
+ iowrite16((u16)out, dpld->dev[id]->txq.tail);
+ } while (cnt--);
+
+ trigger_force_cp_crash(dpld);
+}
+
+static inline u8 *get_tx_buff(struct dpram_link_device *dpld, int id)
+{
+ return dpld->dev[id]->txq.buff;
+}
+
+static inline u32 get_tx_buff_size(struct dpram_link_device *dpld, int id)
+{
+ return dpld->dev[id]->txq.size;
+}
+
+static inline u32 get_rx_head(struct dpram_link_device *dpld, int id)
+{
+ return ioread16(dpld->dev[id]->rxq.head);
+}
+
+static inline u32 get_rx_tail(struct dpram_link_device *dpld, int id)
+{
+ return ioread16(dpld->dev[id]->rxq.tail);
+}
+
+static inline void set_rx_head(struct dpram_link_device *dpld, int id, u32 in)
+{
+ int cnt = 3;
+ u32 val = 0;
+
+ iowrite16((u16)in, dpld->dev[id]->rxq.head);
+
+ do {
+ /* Check head value written */
+ val = ioread16(dpld->dev[id]->rxq.head);
+ if (val == in)
+ return;
+
+ mif_err("ERR: %s rxq.head(%d) != in(%d)\n",
+ get_dev_name(id), val, in);
+ udelay(100);
+
+ /* Write head value again */
+ iowrite16((u16)in, dpld->dev[id]->rxq.head);
+ } while (cnt--);
+
+ trigger_force_cp_crash(dpld);
+}
+
+static inline void set_rx_tail(struct dpram_link_device *dpld, int id, u32 out)
+{
+ int cnt = 3;
+ u32 val = 0;
+
+ iowrite16((u16)out, dpld->dev[id]->rxq.tail);
+
+ do {
+ /* Check tail value written */
+ val = ioread16(dpld->dev[id]->rxq.tail);
+ if (val == out)
+ return;
+
+ mif_err("ERR: %s rxq.tail(%d) != out(%d)\n",
+ get_dev_name(id), val, out);
+ udelay(100);
+
+ /* Write tail value again */
+ iowrite16((u16)out, dpld->dev[id]->rxq.tail);
+ } while (cnt--);
+
+ trigger_force_cp_crash(dpld);
+}
+
+static inline u8 *get_rx_buff(struct dpram_link_device *dpld, int id)
+{
+ return dpld->dev[id]->rxq.buff;
+}
+
+static inline u32 get_rx_buff_size(struct dpram_link_device *dpld, int id)
+{
+ return dpld->dev[id]->rxq.size;
+}
+
+static inline u16 get_mask_req_ack(struct dpram_link_device *dpld, int id)
+{
+ return dpld->dev[id]->mask_req_ack;
+}
+
+static inline u16 get_mask_res_ack(struct dpram_link_device *dpld, int id)
+{
+ return dpld->dev[id]->mask_res_ack;
+}
+
+static inline u16 get_mask_send(struct dpram_link_device *dpld, int id)
+{
+ return dpld->dev[id]->mask_send;
+}
+
+static inline bool dpram_circ_valid(u32 size, u32 in, u32 out)
+{
+ if (in >= size)
+ return false;
+
+ if (out >= size)
+ return false;
+
+ return true;
+}
+
+/* Get free space in the TXQ as well as in & out pointers */
+static inline int dpram_get_txq_space(struct dpram_link_device *dpld, int dev,
+ u32 qsize, u32 *in, u32 *out)
+{
+ struct link_device *ld = &dpld->ld;
+ int cnt = 3;
+ u32 head;
+ u32 tail;
+ int space;
+
+ do {
+ head = get_tx_head(dpld, dev);
+ tail = get_tx_tail(dpld, dev);
+
+ space = (head < tail) ? (tail - head - 1) :
+ (qsize + tail - head - 1);
+ mif_debug("%s: %s_TXQ qsize[%u] in[%u] out[%u] space[%u]\n",
+ ld->name, get_dev_name(dev), qsize, head, tail, space);
+
+ if (dpram_circ_valid(qsize, head, tail)) {
+ *in = head;
+ *out = tail;
+ return space;
+ }
+
+ mif_info("%s: CAUTION! <%pf> "
+ "%s_TXQ invalid (size:%d in:%d out:%d)\n",
+ ld->name, __builtin_return_address(0),
+ get_dev_name(dev), qsize, head, tail);
+
+ udelay(100);
+ } while (cnt--);
+
+ *in = 0;
+ *out = 0;
+ return -EINVAL;
+}
+
+static void dpram_reset_tx_circ(struct dpram_link_device *dpld, int dev)
+{
+ set_tx_head(dpld, dev, 0);
+ set_tx_tail(dpld, dev, 0);
+ if (dev == IPC_FMT)
+ trigger_force_cp_crash(dpld);
+}
+
+/* Get data size in the RXQ as well as in & out pointers */
+static inline int dpram_get_rxq_rcvd(struct dpram_link_device *dpld, int dev,
+ u32 qsize, u32 *in, u32 *out)
+{
+ struct link_device *ld = &dpld->ld;
+ int cnt = 3;
+ u32 head;
+ u32 tail;
+ u32 rcvd;
+
+ do {
+ head = get_rx_head(dpld, dev);
+ tail = get_rx_tail(dpld, dev);
+ if (head == tail) {
+ *in = head;
+ *out = tail;
+ return 0;
+ }
+
+ rcvd = (head > tail) ? (head - tail) : (qsize - tail + head);
+ mif_debug("%s: %s_RXQ qsize[%u] in[%u] out[%u] rcvd[%u]\n",
+ ld->name, get_dev_name(dev), qsize, head, tail, rcvd);
+
+ if (dpram_circ_valid(qsize, head, tail)) {
+ *in = head;
+ *out = tail;
+ return rcvd;
+ }
+
+ mif_info("%s: CAUTION! <%pf> "
+ "%s_RXQ invalid (size:%d in:%d out:%d)\n",
+ ld->name, __builtin_return_address(0),
+ get_dev_name(dev), qsize, head, tail);
+
+ udelay(100);
+ } while (cnt--);
+
+ *in = 0;
+ *out = 0;
+ return -EINVAL;
+}
+
+static void dpram_reset_rx_circ(struct dpram_link_device *dpld, int dev)
+{
+ set_rx_head(dpld, dev, 0);
+ set_rx_tail(dpld, dev, 0);
+ if (dev == IPC_FMT)
+ trigger_force_cp_crash(dpld);
+}
+
/*
** CAUTION : dpram_allow_sleep() MUST be invoked after dpram_wake_up() success
*/
@@ -335,10 +534,21 @@ static int dpram_wake_up(struct dpram_link_device *dpld)
return 0;
if (dpld->dpctl->wakeup() < 0) {
- mif_info("%s: ERR! <%pF> DPRAM wakeup fail\n",
+ mif_err("%s: ERR! <%pf> DPRAM wakeup fail once\n",
ld->name, __builtin_return_address(0));
- return -EACCES;
+
+ if (dpld->dpctl->sleep)
+ dpld->dpctl->sleep();
+
+ udelay(10);
+
+ if (dpld->dpctl->wakeup() < 0) {
+ mif_err("%s: ERR! <%pf> DPRAM wakeup fail twice\n",
+ ld->name, __builtin_return_address(0));
+ return -EACCES;
+ }
}
+
atomic_inc(&dpld->accessing);
return 0;
}
@@ -361,19 +571,19 @@ static int dpram_check_access(struct dpram_link_device *dpld)
{
struct link_device *ld = &dpld->ld;
int i;
- u16 magic = dpld->dpctl->get_magic();
- u16 access = dpld->dpctl->get_access();
+ u16 magic = get_magic(dpld);
+ u16 access = get_access(dpld);
if (likely(magic == DPRAM_MAGIC_CODE && access == 1))
return 0;
- for (i = 1; i <= 10; i++) {
+ for (i = 1; i <= 100; i++) {
mif_info("%s: ERR! magic:%X access:%X -> retry:%d\n",
ld->name, magic, access, i);
- mdelay(1);
+ udelay(100);
- magic = dpld->dpctl->get_magic();
- access = dpld->dpctl->get_access();
+ magic = get_magic(dpld);
+ access = get_access(dpld);
if (likely(magic == DPRAM_MAGIC_CODE && access == 1))
return 0;
}
@@ -388,13 +598,13 @@ static bool dpram_ipc_active(struct dpram_link_device *dpld)
/* Check DPRAM mode */
if (ld->mode != LINK_MODE_IPC) {
- mif_info("%s: ERR! <%pF> ld->mode != LINK_MODE_IPC\n",
+ mif_info("%s: <%pf> ld->mode != LINK_MODE_IPC\n",
ld->name, __builtin_return_address(0));
return false;
}
if (dpram_check_access(dpld) < 0) {
- mif_info("%s: ERR! <%pF> dpram_check_access fail\n",
+ mif_info("%s: ERR! <%pf> dpram_check_access fail\n",
ld->name, __builtin_return_address(0));
return false;
}
@@ -402,224 +612,108 @@ static bool dpram_ipc_active(struct dpram_link_device *dpld)
return true;
}
-static inline bool dpram_circ_valid(u32 size, u32 in, u32 out)
-{
- if (in >= size)
- return false;
-
- if (out >= size)
- return false;
-
- return true;
-}
-
-/* get the size of the TXQ */
-static inline int dpram_get_txq_size(struct dpram_link_device *dpld, int dev)
-{
- return dpld->dpctl->get_tx_buff_size(dev);
-}
-
-/* get in & out pointers of the TXQ */
-static inline void dpram_get_txq_ptrs(struct dpram_link_device *dpld, int dev,
- u32 *in, u32 *out)
-{
- *in = dpld->dpctl->get_tx_head(dev);
- *out = dpld->dpctl->get_tx_tail(dev);
-}
-
-/* get free space in the TXQ as well as in & out pointers */
-static inline int dpram_get_txq_space(struct dpram_link_device *dpld, int dev,
- u32 qsize, u32 *in, u32 *out)
-{
- struct link_device *ld = &dpld->ld;
-
- *in = dpld->dpctl->get_tx_head(dev);
- *out = dpld->dpctl->get_tx_tail(dev);
-
- if (!dpram_circ_valid(qsize, *in, *out)) {
- mif_info("%s: ERR! <%pF> "
- "%s_TXQ invalid (size:%d in:%d out:%d)\n",
- ld->name, __builtin_return_address(0),
- get_dev_name(dev), qsize, *in, *out);
- dpld->dpctl->set_tx_head(dev, 0);
- dpld->dpctl->set_tx_tail(dev, 0);
- *in = 0;
- *out = 0;
- return -EINVAL;
- }
-
- return (*in < *out) ? (*out - *in - 1) : (qsize + *out - *in - 1);
-}
-
static void dpram_ipc_write(struct dpram_link_device *dpld, int dev,
u32 qsize, u32 in, u32 out, struct sk_buff *skb)
{
struct link_device *ld = &dpld->ld;
- struct modemlink_dpram_control *dpctl = dpld->dpctl;
- struct io_device *iod = skbpriv(skb)->iod;
- u8 __iomem *dst = dpctl->get_tx_buff(dev);
+ u8 __iomem *buff = get_tx_buff(dpld, dev);
u8 *src = skb->data;
u32 len = skb->len;
-
- /* check queue status */
- mif_debug("%s: {FMT %u %u %u %u} {RAW %u %u %u %u} ...\n", ld->name,
- dpctl->get_tx_head(IPC_FMT), dpctl->get_tx_tail(IPC_FMT),
- dpctl->get_rx_head(IPC_FMT), dpctl->get_rx_tail(IPC_FMT),
- dpctl->get_tx_head(IPC_RAW), dpctl->get_tx_tail(IPC_RAW),
- dpctl->get_rx_head(IPC_RAW), dpctl->get_rx_tail(IPC_RAW));
-
- if (dev == IPC_FMT) {
- mif_ipc_log(ld->mc, MIF_LNK_TX_EVT, iod, ld, src, len);
- mif_flush_logs(ld->mc);
- }
+ u32 inp;
+ struct mif_irq_map map;
if (in < out) {
/* +++++++++ in ---------- out ++++++++++ */
- memcpy((dst + in), src, len);
+ memcpy((buff + in), src, len);
} else {
/* ------ out +++++++++++ in ------------ */
u32 space = qsize - in;
/* 1) in -> buffer end */
- memcpy((dst + in), src, ((len > space) ? space : len));
+ memcpy((buff + in), src, ((len > space) ? space : len));
/* 2) buffer start -> out */
if (len > space)
- memcpy(dst, (src + space), (len - space));
+ memcpy(buff, (src + space), (len - space));
}
/* update new in pointer */
- in += len;
- if (in >= qsize)
- in -= qsize;
- dpctl->set_tx_head(dev, in);
+ inp = in + len;
+ if (inp >= qsize)
+ inp -= qsize;
+ set_tx_head(dpld, dev, inp);
+
+ if (dev == IPC_FMT) {
+ set_dpram_map(dpld, &map);
+ mif_irq_log(ld->mc->msd, map, "ipc_write", sizeof("ipc_write"));
+ mif_ipc_log(MIF_IPC_AP2CP, ld->mc->msd, skb->data, skb->len);
+ }
+
+ if (ld->aligned && memcmp16_to_io((buff + in), src, 4)) {
+ mif_err("%s: memcmp16_to_io fail\n", ld->name);
+ trigger_force_cp_crash(dpld);
+ }
}
static int dpram_try_ipc_tx(struct dpram_link_device *dpld, int dev)
{
struct link_device *ld = &dpld->ld;
- struct modemlink_dpram_control *dpctl = dpld->dpctl;
struct sk_buff_head *txq = ld->skb_txq[dev];
struct sk_buff *skb;
- u32 qsize = dpram_get_txq_size(dpld, dev);
+ unsigned long int flags;
+ u32 qsize = get_tx_buff_size(dpld, dev);
u32 in;
u32 out;
int space;
int copied = 0;
- u16 mask = 0;
- unsigned long int flags;
- while (1) {
- skb = skb_dequeue(txq);
- if (unlikely(!skb))
- break;
+ spin_lock_irqsave(&dpld->tx_lock[dev], flags);
+ while (1) {
space = dpram_get_txq_space(dpld, dev, qsize, &in, &out);
if (unlikely(space < 0)) {
- skb_queue_head(txq, skb);
- return -ENOSPC;
+ spin_unlock_irqrestore(&dpld->tx_lock[dev], flags);
+ dpram_reset_tx_circ(dpld, dev);
+ return space;
}
+ skb = skb_dequeue(txq);
+ if (unlikely(!skb))
+ break;
+
if (unlikely(space < skb->len)) {
atomic_set(&dpld->res_required[dev], 1);
skb_queue_head(txq, skb);
- mask = dpctl->get_mask_req_ack(dev);
+ spin_unlock_irqrestore(&dpld->tx_lock[dev], flags);
mif_info("%s: %s "
"qsize[%u] in[%u] out[%u] free[%u] < len[%u]\n",
ld->name, get_dev_name(dev),
qsize, in, out, space, skb->len);
- break;
+ return -ENOSPC;
}
- /* TX if there is enough room in the queue
- */
- mif_debug("%s: %s "
- "qsize[%u] in[%u] out[%u] free[%u] >= len[%u]\n",
- ld->name, get_dev_name(dev),
- qsize, in, out, space, skb->len);
-
- spin_lock_irqsave(&dpld->tx_lock, flags);
+ /* TX if there is enough room in the queue */
dpram_ipc_write(dpld, dev, qsize, in, out, skb);
- spin_unlock_irqrestore(&dpld->tx_lock, flags);
-
copied += skb->len;
-
dev_kfree_skb_any(skb);
}
- if (mask)
- return -ENOSPC;
- else
- return copied;
-}
-
-static void dpram_trigger_crash(struct dpram_link_device *dpld)
-{
- struct link_device *ld = &dpld->ld;
- struct io_device *iod;
- int i;
-
- for (i = 0; i < dpld->max_ipc_dev; i++) {
- mif_info("%s: purging %s_skb_txq\b", ld->name, get_dev_name(i));
- skb_queue_purge(ld->skb_txq[i]);
- }
-
- ld->mode = LINK_MODE_ULOAD;
-
- iod = link_get_iod_with_format(ld, IPC_FMT);
- iod->modem_state_changed(iod, STATE_CRASH_EXIT);
-
- iod = link_get_iod_with_format(ld, IPC_BOOT);
- iod->modem_state_changed(iod, STATE_CRASH_EXIT);
-
- iod = link_get_iod_with_channel(ld, PS_DATA_CH_0);
- if (iod)
- iodevs_for_each(&iod->mc->commons, iodev_netif_stop, 0);
-}
-
-static int dpram_trigger_force_cp_crash(struct dpram_link_device *dpld)
-{
- struct link_device *ld = &dpld->ld;
- int ret;
- int cnt = 5000;
-
- mif_info("%s\n", ld->name);
-
- dpld->dpctl->send_intr(INT_CMD(INT_CMD_CRASH_EXIT));
-
- while (cnt--) {
- ret = try_wait_for_completion(&dpld->crash_start_complete);
- if (ret)
- break;
- udelay(1000);
- }
-
- if (!ret) {
- mif_info("%s: ERR! No CRASH_EXIT ACK from CP\n", ld->name);
- dpram_trigger_crash(dpld);
- }
+ spin_unlock_irqrestore(&dpld->tx_lock[dev], flags);
- return 0;
+ return copied;
}
static void dpram_ipc_rx_task(unsigned long data)
{
- struct link_device *ld;
- struct dpram_link_device *dpld;
- struct dpram_rxb *rxb;
+ struct dpram_link_device *dpld = (struct dpram_link_device *)data;
+ struct link_device *ld = &dpld->ld;
struct io_device *iod;
- u32 qlen;
+ struct dpram_rxb *rxb;
+ unsigned qlen;
int i;
- dpld = (struct dpram_link_device *)data;
- ld = &dpld->ld;
-
for (i = 0; i < dpld->max_ipc_dev; i++) {
- if (i == IPC_RAW)
- iod = link_get_iod_with_format(ld, IPC_MULTI_RAW);
- else
- iod = link_get_iod_with_format(ld, i);
-
+ iod = dpld->iod[i];
qlen = rxbq_size(&dpld->rxbq[i]);
while (qlen > 0) {
rxb = rxbq_get_data_rxb(&dpld->rxbq[i]);
@@ -656,37 +750,24 @@ static void dpram_ipc_read(struct dpram_link_device *dpld, int dev, u8 *dst,
ret == 0 : no data
ret > 0 : valid data
*/
-static int dpram_ipc_recv_data(struct dpram_link_device *dpld, int dev,
- u16 non_cmd)
+static int dpram_ipc_recv_data_with_rxb(struct dpram_link_device *dpld, int dev)
{
- struct modemlink_dpram_control *dpctl = dpld->dpctl;
struct link_device *ld = &dpld->ld;
struct dpram_rxb *rxb;
- u8 __iomem *src = dpctl->get_rx_buff(dev);
- u32 in = dpctl->get_rx_head(dev);
- u32 out = dpctl->get_rx_tail(dev);
- u32 qsize = dpctl->get_rx_buff_size(dev);
- u32 rcvd = 0;
-
- if (in == out)
- return 0;
-
- if (dev == IPC_FMT)
- log_dpram_irq(dpld, non_cmd);
-
- /* Get data length in DPRAM*/
- rcvd = (in > out) ? (in - out) : (qsize - out + in);
+ u8 __iomem *src = get_rx_buff(dpld, dev);
+ u32 qsize = get_rx_buff_size(dpld, dev);
+ u32 in;
+ u32 out;
+ u32 rcvd;
+ struct mif_irq_map map;
- mif_debug("%s: %s qsize[%u] in[%u] out[%u] rcvd[%u]\n",
- ld->name, get_dev_name(dev), qsize, in, out, rcvd);
+ rcvd = dpram_get_rxq_rcvd(dpld, dev, qsize, &in, &out);
+ if (rcvd <= 0)
+ return rcvd;
- /* Check each queue */
- if (!dpram_circ_valid(qsize, in, out)) {
- mif_info("%s: ERR! %s_RXQ invalid (size:%d in:%d out:%d)\n",
- ld->name, get_dev_name(dev), qsize, in, out);
- dpctl->set_rx_head(dev, 0);
- dpctl->set_rx_tail(dev, 0);
- return -EINVAL;
+ if (dev == IPC_FMT) {
+ set_dpram_map(dpld, &map);
+ mif_irq_log(ld->mc->msd, map, "ipc_recv", sizeof("ipc_recv"));
}
/* Allocate an rxb */
@@ -704,30 +785,95 @@ static int dpram_ipc_recv_data(struct dpram_link_device *dpld, int dev,
out += rcvd;
if (out >= qsize)
out -= qsize;
- dpctl->set_rx_tail(dev, out);
+ set_rx_tail(dpld, dev, out);
return rcvd;
}
-static void dpram_purge_rx_circ(struct dpram_link_device *dpld, int dev)
+/*
+ ret < 0 : error
+ ret == 0 : no data
+ ret > 0 : valid data
+*/
+static int dpram_ipc_recv_data_with_skb(struct dpram_link_device *dpld, int dev)
{
- u32 in = dpld->dpctl->get_rx_head(dev);
- dpld->dpctl->set_rx_tail(dev, in);
+ struct link_device *ld = &dpld->ld;
+ struct io_device *iod = dpld->iod[dev];
+ struct sk_buff *skb;
+ u8 __iomem *src = get_rx_buff(dpld, dev);
+ u32 qsize = get_rx_buff_size(dpld, dev);
+ u32 in;
+ u32 out;
+ u32 rcvd;
+ int rest;
+ u8 *frm;
+ u8 *dst;
+ unsigned int len;
+ unsigned int pad;
+ unsigned int tot;
+ struct mif_irq_map map;
+
+ rcvd = dpram_get_rxq_rcvd(dpld, dev, qsize, &in, &out);
+ if (rcvd <= 0)
+ return rcvd;
+
+ if (dev == IPC_FMT) {
+ set_dpram_map(dpld, &map);
+ mif_irq_log(ld->mc->msd, map, "ipc_recv", sizeof("ipc_recv"));
+ }
+
+ rest = rcvd;
+ while (rest > 0) {
+ frm = src + out;
+ if (unlikely(!sipc5_start_valid(frm[0]))) {
+ mif_err("%s: ERR! %s invalid start 0x%02X\n",
+ ld->name, get_dev_name(dev), frm[0]);
+ skb_queue_purge(&dpld->skb_rxq[dev]);
+ return -EBADMSG;
+ }
+
+ len = sipc5_get_frame_sz16(frm);
+ if (unlikely(len > rest)) {
+ mif_err("%s: ERR! %s len %d > rest %d\n",
+ ld->name, get_dev_name(dev), len, rest);
+ skb_queue_purge(&dpld->skb_rxq[dev]);
+ return -EBADMSG;
+ }
+
+ pad = sipc5_calc_padding_size(len);
+ tot = len + pad;
+
+ /* Allocate an skb */
+ skb = dev_alloc_skb(tot);
+ if (!skb) {
+ mif_err("%s: ERR! %s dev_alloc_skb fail\n",
+ ld->name, get_dev_name(dev));
+ return -ENOMEM;
+ }
+
+ /* Read data from each DPRAM buffer */
+ dst = skb_put(skb, tot);
+ dpram_ipc_read(dpld, dev, dst, src, out, tot, qsize);
+ skb_trim(skb, len);
+ iod->recv_skb(iod, ld, skb);
+
+ /* Calculate and set new out */
+ rest -= tot;
+ out += tot;
+ if (out >= qsize)
+ out -= qsize;
+ }
+
+ set_rx_tail(dpld, dev, out);
+
+ return rcvd;
}
-static void non_command_handler(struct dpram_link_device *dpld, u16 non_cmd)
+static void non_command_handler(struct dpram_link_device *dpld, u16 intr)
{
- struct modemlink_dpram_control *dpctl = dpld->dpctl;
struct link_device *ld = &dpld->ld;
- struct sk_buff_head *txq;
- struct sk_buff *skb;
- int i;
+ int i = 0;
int ret = 0;
- int copied = 0;
- u32 in;
- u32 out;
- u16 mask = 0;
- u16 req_mask = 0;
u16 tx_mask = 0;
if (!dpram_ipc_active(dpld))
@@ -735,85 +881,154 @@ static void non_command_handler(struct dpram_link_device *dpld, u16 non_cmd)
/* Read data from DPRAM */
for (i = 0; i < dpld->max_ipc_dev; i++) {
- ret = dpram_ipc_recv_data(dpld, i, non_cmd);
+ if (dpld->use_skb)
+ ret = dpram_ipc_recv_data_with_skb(dpld, i);
+ else
+ ret = dpram_ipc_recv_data_with_rxb(dpld, i);
if (ret < 0)
- dpram_purge_rx_circ(dpld, i);
+ dpram_reset_rx_circ(dpld, i);
/* Check and process REQ_ACK (at this time, in == out) */
- if (non_cmd & dpctl->get_mask_req_ack(i)) {
+ if (intr & get_mask_req_ack(dpld, i)) {
mif_debug("%s: send %s_RES_ACK\n",
ld->name, get_dev_name(i));
- mask = dpctl->get_mask_res_ack(i);
- dpctl->send_intr(INT_NON_CMD(mask));
+ tx_mask |= get_mask_res_ack(dpld, i);
}
}
- /* Schedule soft IRQ for RX */
- tasklet_hi_schedule(&dpld->rx_tsk);
+ if (!dpld->use_skb) {
+ /* Schedule soft IRQ for RX */
+ tasklet_hi_schedule(&dpld->rx_tsk);
+ }
/* Try TX via DPRAM */
for (i = 0; i < dpld->max_ipc_dev; i++) {
if (atomic_read(&dpld->res_required[i]) > 0) {
- dpram_get_txq_ptrs(dpld, i, &in, &out);
- if (likely(in == out)) {
- ret = dpram_try_ipc_tx(dpld, i);
- if (ret > 0) {
- atomic_set(&dpld->res_required[i], 0);
- tx_mask |= dpctl->get_mask_send(i);
- } else {
- req_mask |= dpctl->get_mask_req_ack(i);
- }
- } else {
- req_mask |= dpctl->get_mask_req_ack(i);
+ ret = dpram_try_ipc_tx(dpld, i);
+ if (ret > 0) {
+ atomic_set(&dpld->res_required[i], 0);
+ tx_mask |= get_mask_send(dpld, i);
+ } else if (ret == -ENOSPC) {
+ tx_mask |= get_mask_req_ack(dpld, i);
}
}
}
- if (req_mask || tx_mask) {
- tx_mask |= req_mask;
- dpctl->send_intr(INT_NON_CMD(tx_mask));
+ if (tx_mask) {
+ send_intr(dpld, INT_NON_CMD(tx_mask));
mif_debug("%s: send intr 0x%04X\n", ld->name, tx_mask);
}
}
+static void handle_cp_crash(struct dpram_link_device *dpld)
+{
+ struct link_device *ld = &dpld->ld;
+ struct io_device *iod;
+ int i;
+
+ for (i = 0; i < dpld->max_ipc_dev; i++) {
+ mif_info("%s: purging %s_skb_txq\b", ld->name, get_dev_name(i));
+ skb_queue_purge(ld->skb_txq[i]);
+ }
+
+ iod = link_get_iod_with_format(ld, IPC_FMT);
+ iod->modem_state_changed(iod, STATE_CRASH_EXIT);
+
+ iod = link_get_iod_with_format(ld, IPC_BOOT);
+ iod->modem_state_changed(iod, STATE_CRASH_EXIT);
+
+ iod = link_get_iod_with_channel(ld, PS_DATA_CH_0);
+ if (iod)
+ iodevs_for_each(iod->msd, iodev_netif_stop, 0);
+}
+
+static void handle_no_crash_ack(unsigned long arg)
+{
+ struct dpram_link_device *dpld = (struct dpram_link_device *)arg;
+ struct link_device *ld = &dpld->ld;
+
+ mif_err("%s: ERR! No CRASH_EXIT ACK from CP\n", ld->mc->name);
+
+ if (!wake_lock_active(&dpld->wlock))
+ wake_lock(&dpld->wlock);
+
+ handle_cp_crash(dpld);
+}
+
+static int trigger_force_cp_crash(struct dpram_link_device *dpld)
+{
+ struct link_device *ld = &dpld->ld;
+
+ if (ld->mode == LINK_MODE_ULOAD) {
+ mif_err("%s: CP crash is already in progress\n", ld->mc->name);
+ return 0;
+ }
+
+ ld->mode = LINK_MODE_ULOAD;
+ mif_err("%s: called by %pf\n", ld->name, __builtin_return_address(0));
+
+ if (dpld->dp_type == CP_IDPRAM)
+ dpram_wake_up(dpld);
+
+ send_intr(dpld, INT_CMD(INT_CMD_CRASH_EXIT));
+
+ mif_add_timer(&dpld->crash_ack_timer, FORCE_CRASH_ACK_TIMEOUT,
+ handle_no_crash_ack, (unsigned long)dpld);
+
+ return 0;
+}
+
static int dpram_init_ipc(struct dpram_link_device *dpld)
{
struct link_device *ld = &dpld->ld;
- struct modemlink_dpram_control *dpctl = dpld->dpctl;
int i;
if (ld->mode == LINK_MODE_IPC &&
- dpctl->get_magic() == DPRAM_MAGIC_CODE &&
- dpctl->get_access() == 1)
+ get_magic(dpld) == DPRAM_MAGIC_CODE &&
+ get_access(dpld) == 1)
mif_info("%s: IPC already initialized\n", ld->name);
/* Clear pointers in every circular queue */
for (i = 0; i < dpld->max_ipc_dev; i++) {
- dpctl->set_tx_head(i, 0);
- dpctl->set_tx_tail(i, 0);
- dpctl->set_rx_head(i, 0);
- dpctl->set_rx_tail(i, 0);
+ set_tx_head(dpld, i, 0);
+ set_tx_tail(dpld, i, 0);
+ set_rx_head(dpld, i, 0);
+ set_rx_tail(dpld, i, 0);
}
- /* Enable IPC */
- dpctl->set_magic(DPRAM_MAGIC_CODE);
- dpctl->set_access(1);
- if (dpctl->get_magic() != DPRAM_MAGIC_CODE || dpctl->get_access() != 1)
- return -EACCES;
+ /* Initialize variables for efficient TX/RX processing */
+ for (i = 0; i < dpld->max_ipc_dev; i++)
+ dpld->iod[i] = link_get_iod_with_format(ld, i);
+ dpld->iod[IPC_RAW] = link_get_iod_with_format(ld, IPC_MULTI_RAW);
- ld->mode = LINK_MODE_IPC;
+ if (dpld->iod[IPC_RAW]->recv_skb)
+ dpld->use_skb = true;
- for (i = 0; i < dpld->max_ipc_dev; i++)
+ for (i = 0; i < dpld->max_ipc_dev; i++) {
+ spin_lock_init(&dpld->tx_lock[i]);
atomic_set(&dpld->res_required[i], 0);
+ skb_queue_purge(&dpld->skb_rxq[i]);
+ }
+ /* Enable IPC */
atomic_set(&dpld->accessing, 0);
+ set_magic(dpld, DPRAM_MAGIC_CODE);
+ set_access(dpld, 1);
+ if (get_magic(dpld) != DPRAM_MAGIC_CODE || get_access(dpld) != 1)
+ return -EACCES;
+
+ ld->mode = LINK_MODE_IPC;
+
+ if (wake_lock_active(&dpld->wlock))
+ wake_unlock(&dpld->wlock);
+
return 0;
}
static void cmd_req_active_handler(struct dpram_link_device *dpld)
{
- dpld->dpctl->send_intr(INT_CMD(INT_CMD_RES_ACTIVE));
+ send_intr(dpld, INT_CMD(INT_CMD_RES_ACTIVE));
}
static void cmd_crash_reset_handler(struct dpram_link_device *dpld)
@@ -821,10 +1036,13 @@ static void cmd_crash_reset_handler(struct dpram_link_device *dpld)
struct link_device *ld = &dpld->ld;
struct io_device *iod = NULL;
- mif_info("%s: Recv 0xC7 (CRASH_RESET)\n", ld->name);
-
ld->mode = LINK_MODE_ULOAD;
+ if (!wake_lock_active(&dpld->wlock))
+ wake_lock(&dpld->wlock);
+
+ mif_err("%s: Recv 0xC7 (CRASH_RESET)\n", ld->name);
+
iod = link_get_iod_with_format(ld, IPC_FMT);
iod->modem_state_changed(iod, STATE_CRASH_RESET);
@@ -835,19 +1053,36 @@ static void cmd_crash_reset_handler(struct dpram_link_device *dpld)
static void cmd_crash_exit_handler(struct dpram_link_device *dpld)
{
struct link_device *ld = &dpld->ld;
-
- mif_info("%s: Recv 0xC9 (CRASH_EXIT)\n", ld->name);
+ u32 size = dpld->dpctl->dp_size;
+ char *dpram_buff = NULL;
ld->mode = LINK_MODE_ULOAD;
- complete_all(&dpld->crash_start_complete);
+ if (!wake_lock_active(&dpld->wlock))
+ wake_lock(&dpld->wlock);
- if (ld->mdm_data->modem_type == QC_MDM6600) {
- if (dpld->dpctl->log_disp)
- dpld->dpctl->log_disp(dpld->dpctl);
+ mif_err("%s: Recv 0xC9 (CRASH_EXIT)\n", ld->name);
+
+ if (dpld->dp_type == CP_IDPRAM)
+ dpram_wake_up(dpld);
+
+ dpram_buff = kzalloc(size + (MAX_MIF_SEPA_SIZE * 2), GFP_ATOMIC);
+ if (!dpram_buff) {
+ mif_err("DPRAM dump failed!!\n");
+ } else {
+ memset(dpram_buff, 0, size + (MAX_MIF_SEPA_SIZE * 2));
+ memcpy(dpram_buff, MIF_SEPARATOR_DPRAM, MAX_MIF_SEPA_SIZE);
+ memcpy(dpram_buff + MAX_MIF_SEPA_SIZE, &size, sizeof(u32));
+ dpram_buff += (MAX_MIF_SEPA_SIZE * 2);
+ dpram_dump_memory(ld, dpram_buff);
}
- dpram_trigger_crash(dpld);
+ del_timer(&dpld->crash_ack_timer);
+
+ if (dpld->ext_op && dpld->ext_op->crash_log)
+ dpld->ext_op->crash_log(dpld);
+
+ handle_cp_crash(dpld);
}
static void cmd_phone_start_handler(struct dpram_link_device *dpld)
@@ -865,19 +1100,17 @@ static void cmd_phone_start_handler(struct dpram_link_device *dpld)
return;
}
- if (ld->mdm_data->modem_type == SEC_CMC221) {
- if (ld->mc->phone_state != STATE_ONLINE) {
- mif_info("%s: phone_state: %d -> ONLINE\n",
- ld->name, ld->mc->phone_state);
- iod->modem_state_changed(iod, STATE_ONLINE);
- }
- } else if (ld->mdm_data->modem_type == QC_MDM6600) {
- if (dpld->dpctl->phone_boot_start_handler)
- dpld->dpctl->phone_boot_start_handler(dpld->dpctl);
+ if (dpld->ext_op && dpld->ext_op->cp_start_handler)
+ dpld->ext_op->cp_start_handler(dpld);
+
+ if (ld->mc->phone_state != STATE_ONLINE) {
+ mif_info("%s: phone_state: %d -> ONLINE\n",
+ ld->name, ld->mc->phone_state);
+ iod->modem_state_changed(iod, STATE_ONLINE);
}
mif_info("%s: Send 0xC2 (INIT_END)\n", ld->name);
- dpld->dpctl->send_intr(INT_CMD(INT_CMD_INIT_END));
+ send_intr(dpld, INT_CMD(INT_CMD_INIT_END));
}
static void command_handler(struct dpram_link_device *dpld, u16 cmd)
@@ -942,7 +1175,7 @@ static void ext_command_handler(struct dpram_link_device *dpld, u16 cmd)
if (dpld->dpctl->setup_speed) {
dpld->dpctl->setup_speed(DPRAM_SPEED_LOW);
resp = INT_EXT_CMD(EXT_CMD_SET_SPEED_LOW);
- dpld->dpctl->send_intr(resp);
+ send_intr(dpld, resp);
}
break;
@@ -950,7 +1183,7 @@ static void ext_command_handler(struct dpram_link_device *dpld, u16 cmd)
if (dpld->dpctl->setup_speed) {
dpld->dpctl->setup_speed(DPRAM_SPEED_MID);
resp = INT_EXT_CMD(EXT_CMD_SET_SPEED_MID);
- dpld->dpctl->send_intr(resp);
+ send_intr(dpld, resp);
}
break;
@@ -958,7 +1191,7 @@ static void ext_command_handler(struct dpram_link_device *dpld, u16 cmd)
if (dpld->dpctl->setup_speed) {
dpld->dpctl->setup_speed(DPRAM_SPEED_HIGH);
resp = INT_EXT_CMD(EXT_CMD_SET_SPEED_HIGH);
- dpld->dpctl->send_intr(resp);
+ send_intr(dpld, resp);
}
break;
@@ -968,494 +1201,141 @@ static void ext_command_handler(struct dpram_link_device *dpld, u16 cmd)
}
}
-static void cmc22x_idpram_enable_ipc(struct dpram_link_device *dpld)
-{
- dpram_init_ipc(dpld);
-}
-
-static int cmc22x_idpram_wait_response(struct dpram_link_device *dpld, u32 resp)
+static void udl_command_handler(struct dpram_link_device *dpld, u16 cmd)
{
struct link_device *ld = &dpld->ld;
- int count = 50000;
- u32 rcvd = 0;
-
- if (resp == CMC22x_CP_REQ_NV_DATA) {
- while (1) {
- rcvd = ioread32(dpld->bt_map.resp);
- if (rcvd == resp)
- break;
-
- rcvd = dpld->dpctl->recv_msg();
- if (rcvd == 0x9999) {
- mif_info("%s: Invalid resp 0x%04X\n",
- ld->name, rcvd);
- panic("CP Crash ... BAD CRC in CP");
- }
-
- if (count-- < 0) {
- mif_info("%s: Invalid resp 0x%08X\n",
- ld->name, rcvd);
- return -EAGAIN;
- }
-
- udelay(100);
- }
- } else {
- while (1) {
- rcvd = dpld->dpctl->recv_msg();
-
- if (rcvd == resp)
- break;
-
- if (resp == CMC22x_CP_RECV_NV_END &&
- rcvd == CMC22x_CP_CAL_BAD) {
- mif_info("%s: CMC22x_CP_CAL_BAD\n", ld->name);
- break;
- }
-
- if (count-- < 0) {
- mif_info("%s: Invalid resp 0x%04X\n",
- ld->name, rcvd);
- return -EAGAIN;
- }
-
- udelay(100);
- }
- }
-
- return rcvd;
-}
-
-static int cmc22x_idpram_send_boot(struct link_device *ld, unsigned long arg)
-{
- int err = 0;
- int cnt = 0;
- struct dpram_link_device *dpld = to_dpram_link_device(ld);
- u8 __iomem *bt_buff = dpld->bt_map.buff;
- struct dpram_boot_img cp_img;
- u8 *img_buff = NULL;
-
- ld->mode = LINK_MODE_BOOT;
-
- dpld->dpctl->setup_speed(DPRAM_SPEED_LOW);
-
- /* Test memory... After testing, memory is cleared. */
- if (test_dpram(ld->name, bt_buff, dpld->bt_map.size) < 0) {
- mif_info("%s: ERR! test_dpram fail!\n", ld->name);
- ld->mode = LINK_MODE_INVALID;
- return -EIO;
- }
-
- /* Get information about the boot image */
- err = copy_from_user((struct dpram_boot_img *)&cp_img, (void *)arg,
- sizeof(struct dpram_boot_img));
- mif_info("%s: CP image addr = 0x%08X, size = %d\n",
- ld->name, (int)cp_img.addr, cp_img.size);
-
- /* Alloc a buffer for the boot image */
- img_buff = kzalloc(dpld->bt_map.size, GFP_KERNEL);
- if (!img_buff) {
- mif_info("%s: ERR! kzalloc fail\n", ld->name);
- ld->mode = LINK_MODE_INVALID;
- return -ENOMEM;
- }
- /* Copy boot image from the user space to the image buffer */
- err = copy_from_user(img_buff, cp_img.addr, cp_img.size);
-
- /* Copy boot image to DPRAM and verify it */
- memcpy(bt_buff, img_buff, cp_img.size);
- if (memcmp16_to_io(bt_buff, img_buff, cp_img.size)) {
- mif_info("%s: ERR! Boot may be broken!!!\n", ld->name);
- goto err;
+ if (cmd & UDL_RESULT_FAIL) {
+ mif_info("%s: ERR! Command failed: %04x\n", ld->name, cmd);
+ return;
}
- dpld->dpctl->reset();
- udelay(1000);
-
- if (cp_img.mode == CMC22x_BOOT_MODE_NORMAL) {
- mif_info("%s: CMC22x_BOOT_MODE_NORMAL\n", ld->name);
- mif_info("%s: Send req 0x%08X\n", ld->name, cp_img.req);
- iowrite32(cp_img.req, dpld->bt_map.req);
-
- /* Wait for cp_img.resp for 1 second */
- mif_info("%s: Wait resp 0x%08X\n", ld->name, cp_img.resp);
- while (ioread32(dpld->bt_map.resp) != cp_img.resp) {
- cnt++;
- msleep_interruptible(10);
- if (cnt > 100) {
- mif_info("%s: ERR! Invalid resp 0x%08X\n",
- ld->name, ioread32(dpld->bt_map.resp));
- goto err;
- }
- }
- } else {
- mif_info("%s: CMC22x_BOOT_MODE_DUMP\n", ld->name);
+ switch (UDL_CMD_MASK(cmd)) {
+ case UDL_CMD_RECV_READY:
+ mif_debug("%s: Send CP-->AP RECEIVE_READY\n", ld->name);
+ send_intr(dpld, CMD_IMG_START_REQ);
+ break;
+ default:
+ complete_all(&dpld->udl_cmd_complete);
}
-
- kfree(img_buff);
-
- mif_info("%s: Send BOOT done\n", ld->name);
-
- if (dpld->dpctl->setup_speed)
- dpld->dpctl->setup_speed(DPRAM_SPEED_HIGH);
-
- return 0;
-
-err:
- ld->mode = LINK_MODE_INVALID;
- kfree(img_buff);
-
- mif_info("%s: ERR! Boot send fail!!!\n", ld->name);
- return -EIO;
}
-static int cmc22x_idpram_send_main(struct link_device *ld, struct sk_buff *skb)
+static inline void dpram_ipc_rx(struct dpram_link_device *dpld, u16 intr)
{
- int err = 0;
- int ret = 0;
- struct dpram_link_device *dpld = to_dpram_link_device(ld);
- struct dpram_boot_frame *bf = (struct dpram_boot_frame *)skb->data;
- u8 __iomem *buff = (dpld->bt_map.buff + bf->offset);
-
- if ((bf->offset + bf->len) > dpld->bt_map.size) {
- mif_info("%s: ERR! Out of DPRAM boundary\n", ld->name);
- err = -EINVAL;
- goto exit;
- }
-
- if (bf->len)
- memcpy(buff, bf->data, bf->len);
-
- if (bf->request)
- dpld->dpctl->send_msg((u16)bf->request);
-
- if (bf->response) {
- err = cmc22x_idpram_wait_response(dpld, bf->response);
- if (err < 0)
- mif_info("%s: ERR! wait_response fail (err %d)\n",
- ld->name, err);
- }
-
- if (bf->request == CMC22x_CAL_NV_DOWN_END) {
- mif_info("%s: CMC22x_CAL_NV_DOWN_END\n", ld->name);
- cmc22x_idpram_enable_ipc(dpld);
- }
-
-exit:
- if (err < 0)
- ret = err;
+ if (unlikely(INT_CMD_VALID(intr)))
+ command_handler(dpld, intr);
else
- ret = skb->len;
-
- dev_kfree_skb_any(skb);
-
- return ret;
+ non_command_handler(dpld, intr);
}
-static void cmc22x_idpram_wait_dump(unsigned long arg)
+static inline void dpram_intr_handler(struct dpram_link_device *dpld, u16 intr)
{
- struct dpram_link_device *dpld = (struct dpram_link_device *)arg;
- u16 msg;
-
- if (!dpld) {
- mif_info("ERR! dpld == NULL\n");
- return;
- }
+ char *name = dpld->ld.name;
- msg = dpld->dpctl->recv_msg();
-
- if (msg == CMC22x_CP_DUMP_END) {
- complete_all(&dpld->dump_recv_done);
- return;
- }
-
- if (((dpld->dump_rcvd & 0x1) == 0) && (msg == CMC22x_1ST_BUFF_FULL)) {
- complete_all(&dpld->dump_recv_done);
+ if (unlikely(intr == INT_POWERSAFE_FAIL)) {
+ mif_info("%s: intr == INT_POWERSAFE_FAIL\n", name);
return;
}
- if (((dpld->dump_rcvd & 0x1) == 1) && (msg == CMC22x_2ND_BUFF_FULL)) {
- complete_all(&dpld->dump_recv_done);
+ if (unlikely(EXT_UDL_CMD(intr))) {
+ if (likely(EXT_INT_VALID(intr))) {
+ if (UDL_CMD_VALID(intr))
+ udl_command_handler(dpld, intr);
+ else if (EXT_CMD_VALID(intr))
+ ext_command_handler(dpld, intr);
+ else
+ mif_info("%s: ERR! invalid intr 0x%04X\n",
+ name, intr);
+ } else {
+ mif_info("%s: ERR! invalid intr 0x%04X\n", name, intr);
+ }
return;
}
- mif_add_timer(&dpld->dump_timer, CMC22x_DUMP_WAIT_TIMEOVER,
- cmc22x_idpram_wait_dump, (unsigned long)dpld);
-}
-
-static int cmc22x_idpram_upload(struct dpram_link_device *dpld,
- struct dpram_dump_arg *dumparg)
-{
- struct link_device *ld = &dpld->ld;
- int ret;
- u8 __iomem *src;
- int buff_size = CMC22x_DUMP_BUFF_SIZE;
-
- if ((dpld->dump_rcvd & 0x1) == 0)
- dpld->dpctl->send_msg(CMC22x_1ST_BUFF_READY);
- else
- dpld->dpctl->send_msg(CMC22x_2ND_BUFF_READY);
-
- init_completion(&dpld->dump_recv_done);
-
- mif_add_timer(&dpld->dump_timer, CMC22x_DUMP_WAIT_TIMEOVER,
- cmc22x_idpram_wait_dump, (unsigned long)dpld);
-
- ret = wait_for_completion_interruptible_timeout(
- &dpld->dump_recv_done, DUMP_TIMEOUT);
- if (!ret) {
- mif_info("%s: ERR! CP didn't send dump data!!!\n", ld->name);
- goto err_out;
- }
-
- if (dpld->dpctl->recv_msg() == CMC22x_CP_DUMP_END) {
- mif_info("%s: CMC22x_CP_DUMP_END\n", ld->name);
- wake_unlock(&dpld->dpram_wake_lock);
- return 0;
- }
-
- if ((dpld->dump_rcvd & 0x1) == 0)
- src = dpld->ul_map.buff;
+ if (likely(INT_VALID(intr)))
+ dpram_ipc_rx(dpld, intr);
else
- src = dpld->ul_map.buff + CMC22x_DUMP_BUFF_SIZE;
-
- memcpy(dpld->buff, src, buff_size);
-
- ret = copy_to_user(dumparg->buff, dpld->buff, buff_size);
- if (ret < 0) {
- mif_info("%s: ERR! copy_to_user fail\n", ld->name);
- goto err_out;
- }
-
- dpld->dump_rcvd++;
- return buff_size;
-
-err_out:
- wake_unlock(&dpld->dpram_wake_lock);
- return -EIO;
+ mif_info("%s: ERR! invalid intr 0x%04X\n", name, intr);
}
-static int cbp72_edpram_wait_response(struct dpram_link_device *dpld, u32 resp)
+static irqreturn_t ap_idpram_irq_handler(int irq, void *data)
{
- struct link_device *ld = &dpld->ld;
- int ret;
- int int2cp;
+ struct dpram_link_device *dpld = (struct dpram_link_device *)data;
+ struct link_device *ld = (struct link_device *)&dpld->ld;
+ u16 int2ap = recv_intr(dpld);
- ret = wait_for_completion_interruptible_timeout(
- &dpld->udl_cmd_complete, UDL_TIMEOUT);
- if (!ret) {
- mif_info("%s: ERR! No UDL_CMD_RESP!!!\n", ld->name);
- return -ENXIO;
- }
-
- int2cp = dpld->dpctl->recv_intr();
- mif_debug("%s: int2cp = 0x%x\n", ld->name, int2cp);
- if (resp == int2cp || int2cp == 0xA700)
- return int2cp;
- else
- return -EINVAL;
-}
-
-static int cbp72_edpram_send_bin(struct link_device *ld, struct sk_buff *skb)
-{
- struct dpram_link_device *dpld = to_dpram_link_device(ld);
- struct dpram_boot_frame *bf = (struct dpram_boot_frame *)skb->data;
- u8 __iomem *buff = dpld->bt_map.buff;
- int err = 0;
-
- if (bf->len > dpld->bt_map.size) {
- mif_info("%s: ERR! Out of DPRAM boundary\n", ld->name);
- err = -EINVAL;
- goto exit;
- }
-
- if (bf->len)
- memcpy(buff, bf->data, bf->len);
-
- init_completion(&dpld->udl_cmd_complete);
+ if (unlikely(ld->mode == LINK_MODE_OFFLINE))
+ return IRQ_HANDLED;
- if (bf->request)
- dpld->dpctl->send_intr((u16)bf->request);
-
- if (bf->response) {
- err = cbp72_edpram_wait_response(dpld, bf->response);
- if (err < 0) {
- mif_info("%s: ERR! wait_response fail (%d)\n",
- ld->name, err);
- goto exit;
- } else if (err == bf->response) {
- err = skb->len;
- }
- }
+ dpram_intr_handler(dpld, int2ap);
-exit:
- dev_kfree_skb_any(skb);
- return err;
+ return IRQ_HANDLED;
}
-static int dpram_upload(struct dpram_link_device *dpld,
- struct dpram_dump_arg *dump, unsigned char __user *target)
+static irqreturn_t cp_idpram_irq_handler(int irq, void *data)
{
- struct link_device *ld = &dpld->ld;
- struct ul_header header;
- u8 *dest;
- u8 *buff = vmalloc(DP_DEFAULT_DUMP_LEN);
- u16 plen = 0;
- int err = 0;
- int ret = 0;
- int buff_size = 0;
-
- mif_info("\n");
+ struct dpram_link_device *dpld = (struct dpram_link_device *)data;
+ struct link_device *ld = (struct link_device *)&dpld->ld;
+ u16 int2ap;
- wake_lock(&dpld->dpram_wake_lock);
- init_completion(&dpld->udl_cmd_complete);
-
- mif_info("%s: req = %x, resp =%x", ld->name, dump->req, dump->resp);
-
- if (dump->req)
- dpld->dpctl->send_intr((u16)dump->req);
-
- if (dump->resp) {
- err = cbp72_edpram_wait_response(dpld, dump->resp);
- if (err < 0) {
- mif_info("%s: ERR! wait_response fail (%d)\n",
- ld->name, err);
- goto exit;
- }
- }
-
- if (dump->cmd)
- return err;
-
- dest = (u8 *)dpld->ul_map.buff;
-
- header.bop = *(u8 *)(dest);
- header.total_frame = *(u16 *)(dest + 1);
- header.curr_frame = *(u16 *)(dest + 3);
- header.len = *(u16 *)(dest + 5);
-
- mif_info("%s: total frame:%d, current frame:%d, data len:%d\n",
- ld->name, header.total_frame, header.curr_frame, header.len);
-
- plen = min_t(u16, header.len, DP_DEFAULT_DUMP_LEN);
-
- memcpy(buff, dest + sizeof(struct ul_header), plen);
- ret = copy_to_user(dump->buff, buff, plen);
- if (ret < 0) {
- mif_info("%s: copy_to_user fail\n", ld->name);
- goto exit;
- }
- buff_size = plen;
+ if (unlikely(ld->mode == LINK_MODE_OFFLINE))
+ return IRQ_HANDLED;
- ret = copy_to_user(target + 4, &buff_size, sizeof(int));
- if (ret < 0) {
- mif_info("%s: copy_to_user fail\n", ld->name);
- goto exit;
+ if (dpram_wake_up(dpld) < 0) {
+ log_dpram_status(dpld);
+ trigger_force_cp_crash(dpld);
+ return IRQ_HANDLED;
}
- wake_unlock(&dpld->dpram_wake_lock);
+ int2ap = recv_intr(dpld);
- return err;
+ dpram_intr_handler(dpld, int2ap);
-exit:
- vfree(buff);
- iowrite32(0, dpld->ul_map.magic);
- wake_unlock(&dpld->dpram_wake_lock);
- return -EIO;
-}
+ clear_intr(dpld);
-static void udl_cmd_handler(struct dpram_link_device *dpld, u16 cmd)
-{
- struct link_device *ld = &dpld->ld;
-
- if (cmd & UDL_RESULT_FAIL) {
- mif_info("%s: ERR! Command failed: %04x\n", ld->name, cmd);
- return;
- }
+ dpram_allow_sleep(dpld);
- switch (UDL_CMD_MASK(cmd)) {
- case UDL_CMD_RECEIVE_READY:
- mif_debug("%s: Send CP-->AP RECEIVE_READY\n", ld->name);
- dpld->dpctl->send_intr(CMD_IMG_START_REQ);
- break;
- default:
- complete_all(&dpld->udl_cmd_complete);
- }
+ return IRQ_HANDLED;
}
-static irqreturn_t dpram_irq_handler(int irq, void *data)
+static irqreturn_t ext_dpram_irq_handler(int irq, void *data)
{
- struct link_device *ld = (struct link_device *)data;
- struct dpram_link_device *dpld = to_dpram_link_device(ld);
- u16 int2ap = 0;
+ struct dpram_link_device *dpld = (struct dpram_link_device *)data;
+ struct link_device *ld = (struct link_device *)&dpld->ld;
+ u16 int2ap = recv_intr(dpld);
- if (!ld->mc || ld->mc->phone_state == STATE_OFFLINE)
+ if (unlikely(ld->mode == LINK_MODE_OFFLINE))
return IRQ_HANDLED;
- if (dpram_wake_up(dpld) < 0)
- return IRQ_HANDLED;
-
- int2ap = dpld->dpctl->recv_intr();
-
- if (dpld->dpctl->clear_intr)
- dpld->dpctl->clear_intr();
-
- if (int2ap == INT_POWERSAFE_FAIL) {
- mif_info("%s: int2ap == INT_POWERSAFE_FAIL\n", ld->name);
- goto exit_isr;
- }
-
- if (ld->mdm_data->modem_type == QC_MDM6600) {
- if ((int2ap == 0x1234)|(int2ap == 0xDBAB)|(int2ap == 0xABCD)) {
- if (dpld->dpctl->dload_cmd_hdlr)
- dpld->dpctl->dload_cmd_hdlr(dpld->dpctl,
- int2ap);
- goto exit_isr;
- }
- }
-
- if (UDL_CMD_VALID(int2ap))
- udl_cmd_handler(dpld, int2ap);
- else if (EXT_INT_VALID(int2ap) && EXT_CMD_VALID(int2ap))
- ext_command_handler(dpld, int2ap);
- else if (INT_CMD_VALID(int2ap))
- command_handler(dpld, int2ap);
- else if (INT_VALID(int2ap))
- non_command_handler(dpld, int2ap);
- else
- mif_info("%s: ERR! invalid intr 0x%04X\n", ld->name, int2ap);
+ dpram_intr_handler(dpld, int2ap);
-exit_isr:
- dpram_allow_sleep(dpld);
return IRQ_HANDLED;
}
-static void dpram_send_ipc(struct link_device *ld, int dev, struct sk_buff *skb)
+static void dpram_send_ipc(struct link_device *ld, int dev,
+ struct io_device *iod, struct sk_buff *skb)
{
struct dpram_link_device *dpld = to_dpram_link_device(ld);
- struct sk_buff_head *txq;
+ struct sk_buff_head *txq = ld->skb_txq[dev];
int ret;
u16 mask;
- if (unlikely(dev >= dpld->max_ipc_dev)) {
- mif_info("%s: ERR! dev %d >= max_ipc_dev(%s)\n",
- ld->name, dev, get_dev_name(dpld->max_ipc_dev));
- return;
+ skb_queue_tail(txq, skb);
+ if (txq->qlen > 1024) {
+ mif_debug("%s: %s txq->qlen %d > 1024\n",
+ ld->name, get_dev_name(dev), txq->qlen);
}
- if (dpram_wake_up(dpld) < 0)
- return;
+ if (dpld->dp_type == CP_IDPRAM) {
+ if (dpram_wake_up(dpld) < 0) {
+ trigger_force_cp_crash(dpld);
+ return;
+ }
+ }
if (!dpram_ipc_active(dpld))
goto exit;
- txq = ld->skb_txq[dev];
- if (txq->qlen > 1024)
- mif_info("%s: txq->qlen %d > 1024\n", ld->name, txq->qlen);
-
- skb_queue_tail(txq, skb);
-
if (atomic_read(&dpld->res_required[dev]) > 0) {
mif_debug("%s: %s_TXQ is full\n", ld->name, get_dev_name(dev));
goto exit;
@@ -1463,46 +1343,51 @@ static void dpram_send_ipc(struct link_device *ld, int dev, struct sk_buff *skb)
ret = dpram_try_ipc_tx(dpld, dev);
if (ret > 0) {
- mask = dpld->dpctl->get_mask_send(dev);
- dpld->dpctl->send_intr(INT_NON_CMD(mask));
- } else {
- mask = dpld->dpctl->get_mask_req_ack(dev);
- dpld->dpctl->send_intr(INT_NON_CMD(mask));
+ mask = get_mask_send(dpld, dev);
+ send_intr(dpld, INT_NON_CMD(mask));
+ } else if (ret == -ENOSPC) {
+ mask = get_mask_req_ack(dpld, dev);
+ send_intr(dpld, INT_NON_CMD(mask));
mif_info("%s: Send REQ_ACK 0x%04X\n", ld->name, mask);
+ } else {
+ mif_info("%s: dpram_try_ipc_tx fail (err %d)\n", ld->name, ret);
}
exit:
- dpram_allow_sleep(dpld);
+ if (dpld->dp_type == CP_IDPRAM)
+ dpram_allow_sleep(dpld);
}
-static int dpram_send_binary(struct link_device *ld, struct sk_buff *skb)
+static int dpram_send_cp_binary(struct link_device *ld, struct sk_buff *skb)
{
- int err = 0;
-
- if (ld->mdm_data->modem_type == SEC_CMC221)
- err = cmc22x_idpram_send_main(ld, skb);
- else if (ld->mdm_data->modem_type == VIA_CBP72)
- err = cbp72_edpram_send_bin(ld, skb);
+ struct dpram_link_device *dpld = to_dpram_link_device(ld);
- return err;
+ if (dpld->ext_op && dpld->ext_op->download_binary)
+ return dpld->ext_op->download_binary(dpld, skb);
+ else
+ return -ENODEV;
}
static int dpram_send(struct link_device *ld, struct io_device *iod,
struct sk_buff *skb)
{
- enum dev_format fmt = iod->format;
+ enum dev_format dev = iod->format;
int len = skb->len;
- switch (fmt) {
+ switch (dev) {
case IPC_FMT:
case IPC_RAW:
case IPC_RFS:
- if (likely(ld->mc->phone_state == STATE_ONLINE))
- dpram_send_ipc(ld, fmt, skb);
+ if (likely(ld->mode == LINK_MODE_IPC)) {
+ dpram_send_ipc(ld, dev, iod, skb);
+ } else {
+ mif_info("%s: ld->mode != LINK_MODE_IPC\n", ld->name);
+ dev_kfree_skb_any(skb);
+ }
return len;
case IPC_BOOT:
- return dpram_send_binary(ld, skb);
+ return dpram_send_cp_binary(ld, skb);
default:
mif_info("%s: ERR! no TXQ for %s\n", ld->name, iod->name);
@@ -1511,291 +1396,252 @@ static int dpram_send(struct link_device *ld, struct io_device *iod,
}
}
-static int dpram_set_dl_magic(struct link_device *ld, struct io_device *iod)
+static int dpram_force_dump(struct link_device *ld, struct io_device *iod)
{
struct dpram_link_device *dpld = to_dpram_link_device(ld);
-
- ld->mode = LINK_MODE_DLOAD;
-
- iowrite32(DP_MAGIC_DMDL, dpld->dl_map.magic);
-
+ trigger_force_cp_crash(dpld);
return 0;
}
-static int dpram_force_dump(struct link_device *ld, struct io_device *iod)
+static void dpram_dump_memory(struct link_device *ld, char *buff)
{
struct dpram_link_device *dpld = to_dpram_link_device(ld);
+ u8 __iomem *base = dpld->dpctl->dp_base;
+ u32 size = dpld->dpctl->dp_size;
- mif_info("%s\n", ld->name);
-
- if (dpram_wake_up(dpld) < 0)
- mif_info("%s: WARNING! dpram_wake_up fail\n", ld->name);
-
- dpram_trigger_force_cp_crash(dpld);
-
- dpram_allow_sleep(dpld);
+ if (dpld->dp_type == CP_IDPRAM)
+ dpram_wake_up(dpld);
- return 0;
+ memcpy(buff, base, size);
}
-static int dpram_set_ul_magic(struct link_device *ld, struct io_device *iod)
+static int dpram_dump_start(struct link_device *ld, struct io_device *iod)
{
struct dpram_link_device *dpld = to_dpram_link_device(ld);
- u8 *dest = dpld->ul_map.buff;
-
- ld->mode = LINK_MODE_ULOAD;
-
- if (ld->mdm_data->modem_type == SEC_CMC221) {
- wake_lock(&dpld->dpram_wake_lock);
- dpld->dump_rcvd = 0;
- iowrite32(CMC22x_CP_DUMP_MAGIC, dpld->ul_map.magic);
- } else {
- iowrite32(DP_MAGIC_UMDL, dpld->ul_map.magic);
- iowrite8((u8)START_INDEX, dest + 0);
- iowrite8((u8)0x1, dest + 1);
- iowrite8((u8)0x1, dest + 2);
- iowrite8((u8)0x0, dest + 3);
- iowrite8((u8)END_INDEX, dest + 4);
- }
-
- init_completion(&dpld->dump_start_complete);
-
- return 0;
+ if (dpld->ext_op && dpld->ext_op->dump_start)
+ return dpld->ext_op->dump_start(dpld);
+ else
+ return -ENODEV;
}
static int dpram_dump_update(struct link_device *ld, struct io_device *iod,
unsigned long arg)
{
struct dpram_link_device *dpld = to_dpram_link_device(ld);
- struct dpram_dump_arg dump;
- int ret;
-
- ret = copy_from_user(&dump, (void __user *)arg, sizeof(dump));
- if (ret < 0) {
- mif_info("%s: ERR! copy_from_user fail\n", ld->name);
- return ret;
- }
- if (ld->mdm_data->modem_type == SEC_CMC221)
- return cmc22x_idpram_upload(dpld, &dump);
+ if (dpld->ext_op && dpld->ext_op->dump_update)
+ return dpld->ext_op->dump_update(dpld, (void *)arg);
else
- return dpram_upload(dpld, &dump, (unsigned char __user *)arg);
+ return -ENODEV;
}
-static int dpram_link_ioctl(struct link_device *ld, struct io_device *iod,
+static int dpram_ioctl(struct link_device *ld, struct io_device *iod,
unsigned int cmd, unsigned long arg)
{
- int err = -EFAULT;
struct dpram_link_device *dpld = to_dpram_link_device(ld);
+ int err = 0;
mif_info("%s: cmd 0x%08X\n", ld->name, cmd);
switch (cmd) {
- case IOCTL_DPRAM_SEND_BOOT:
- err = cmc22x_idpram_send_boot(ld, arg);
- if (err < 0) {
- mif_info("%s: ERR! dpram_send_boot fail\n", ld->name);
- goto exit;
- }
- break;
+ case IOCTL_DPRAM_INIT_STATUS:
+ mif_debug("%s: get dpram init status\n", ld->name);
+ return dpld->dpram_init_status;
- case IOCTL_DPRAM_PHONE_POWON:
- if (dpld->dpctl->cpimage_load_prepare) {
- err = dpld->dpctl->cpimage_load_prepare(dpld->dpctl);
- if (err < 0) {
- mif_info("%s: ERR! cpimage_load_prepare fail\n",
- ld->name);
- goto exit;
- }
+ default:
+ if (dpld->ext_ioctl) {
+ err = dpld->ext_ioctl(dpld, iod, cmd, arg);
+ } else {
+ mif_err("%s: ERR! invalid cmd 0x%08X\n", ld->name, cmd);
+ err = -EINVAL;
}
- break;
- case IOCTL_DPRAM_PHONEIMG_LOAD:
- if (dpld->dpctl->cpimage_load) {
- err = dpld->dpctl->cpimage_load(
- (void *)arg, dpld->dpctl);
- if (err < 0) {
- mif_info("%s: ERR! cpimage_load fail\n",
- ld->name);
- goto exit;
- }
- }
break;
+ }
- case IOCTL_DPRAM_NVDATA_LOAD:
- if (dpld->dpctl->nvdata_load) {
- err = dpld->dpctl->nvdata_load(
- (void *)arg, dpld->dpctl);
- if (err < 0) {
- mif_info("%s: ERR! nvdata_load fail\n",
- ld->name);
- goto exit;
- }
- }
- break;
+ return err;
+}
- case IOCTL_DPRAM_PHONE_BOOTSTART:
- if (dpld->dpctl->phone_boot_start) {
- err = dpld->dpctl->phone_boot_start(dpld->dpctl);
- if (err < 0) {
- mif_info("%s: ERR! phone_boot_start fail\n",
- ld->name);
- goto exit;
- }
- }
- if (dpld->dpctl->phone_boot_start_post_process) {
- err = dpld->dpctl->phone_boot_start_post_process();
- if (err < 0) {
- mif_info("%s: ERR! "
- "phone_boot_start_post_process fail\n",
- ld->name);
- goto exit;
- }
- }
- break;
+static void dpram_remap_std_16k_region(struct dpram_link_device *dpld)
+{
+ struct dpram_ipc_16k_map *dpram_map;
+ struct dpram_ipc_device *dev;
- case IOCTL_DPRAM_PHONE_UPLOAD_STEP1:
- disable_irq_nosync(dpld->irq);
-
- if (dpld->dpctl->cpupload_step1) {
- err = dpld->dpctl->cpupload_step1(dpld->dpctl);
- if (err < 0) {
- dpld->dpctl->clear_intr();
- enable_irq(dpld->irq);
- mif_info("%s: ERR! cpupload_step1 fail\n",
- ld->name);
- goto exit;
- }
- }
- break;
+ dpram_map = (struct dpram_ipc_16k_map *)dpld->dp_base;
- case IOCTL_DPRAM_PHONE_UPLOAD_STEP2:
- if (dpld->dpctl->cpupload_step2) {
- err = dpld->dpctl->cpupload_step2(
- (void *)arg, dpld->dpctl);
- if (err < 0) {
- dpld->dpctl->clear_intr();
- enable_irq(dpld->irq);
- mif_info("%s: ERR! cpupload_step2 fail\n",
- ld->name);
- goto exit;
- }
- }
- break;
+ /* magic code and access enable fields */
+ dpld->ipc_map.magic = (u16 __iomem *)&dpram_map->magic;
+ dpld->ipc_map.access = (u16 __iomem *)&dpram_map->access;
- case IOCTL_DPRAM_INIT_STATUS:
- mif_debug("%s: get dpram init status\n", ld->name);
- return dpld->dpram_init_status;
+ /* FMT */
+ dev = &dpld->ipc_map.dev[IPC_FMT];
- case IOCTL_MODEM_DL_START:
- err = dpram_set_dl_magic(ld, iod);
- if (err < 0) {
- mif_info("%s: ERR! dpram_set_dl_magic fail\n",
- ld->name);
- goto exit;
- }
+ strcpy(dev->name, "FMT");
+ dev->id = IPC_FMT;
- default:
- break;
- }
+ dev->txq.head = (u16 __iomem *)&dpram_map->fmt_tx_head;
+ dev->txq.tail = (u16 __iomem *)&dpram_map->fmt_tx_tail;
+ dev->txq.buff = (u8 __iomem *)&dpram_map->fmt_tx_buff[0];
+ dev->txq.size = DP_16K_FMT_TX_BUFF_SZ;
- return 0;
+ dev->rxq.head = (u16 __iomem *)&dpram_map->fmt_rx_head;
+ dev->rxq.tail = (u16 __iomem *)&dpram_map->fmt_rx_tail;
+ dev->rxq.buff = (u8 __iomem *)&dpram_map->fmt_rx_buff[0];
+ dev->rxq.size = DP_16K_FMT_RX_BUFF_SZ;
-exit:
- return err;
+ dev->mask_req_ack = INT_MASK_REQ_ACK_F;
+ dev->mask_res_ack = INT_MASK_RES_ACK_F;
+ dev->mask_send = INT_MASK_SEND_F;
+
+ /* RAW */
+ dev = &dpld->ipc_map.dev[IPC_RAW];
+
+ strcpy(dev->name, "RAW");
+ dev->id = IPC_RAW;
+
+ dev->txq.head = (u16 __iomem *)&dpram_map->raw_tx_head;
+ dev->txq.tail = (u16 __iomem *)&dpram_map->raw_tx_tail;
+ dev->txq.buff = (u8 __iomem *)&dpram_map->raw_tx_buff[0];
+ dev->txq.size = DP_16K_RAW_TX_BUFF_SZ;
+
+ dev->rxq.head = (u16 __iomem *)&dpram_map->raw_rx_head;
+ dev->rxq.tail = (u16 __iomem *)&dpram_map->raw_rx_tail;
+ dev->rxq.buff = (u8 __iomem *)&dpram_map->raw_rx_buff[0];
+ dev->rxq.size = DP_16K_RAW_RX_BUFF_SZ;
+
+ dev->mask_req_ack = INT_MASK_REQ_ACK_R;
+ dev->mask_res_ack = INT_MASK_RES_ACK_R;
+ dev->mask_send = INT_MASK_SEND_R;
+
+ /* interrupt ports */
+ dpld->ipc_map.mbx_cp2ap = (u16 __iomem *)&dpram_map->mbx_cp2ap;
+ dpld->ipc_map.mbx_ap2cp = (u16 __iomem *)&dpram_map->mbx_ap2cp;
}
-static void dpram_table_init(struct dpram_link_device *dpld)
+static int dpram_table_init(struct dpram_link_device *dpld)
{
- struct link_device *ld;
+ struct link_device *ld = &dpld->ld;
u8 __iomem *dp_base;
-
- if (!dpld) {
- mif_info("ERR! dpld == NULL\n");
- return;
- }
- ld = &dpld->ld;
+ int i;
if (!dpld->dp_base) {
mif_info("%s: ERR! dpld->dp_base == NULL\n", ld->name);
- return;
+ return -EINVAL;
}
-
dp_base = dpld->dp_base;
+ /* Map for IPC */
+ if (dpld->dpctl->ipc_map) {
+ memcpy(&dpld->ipc_map, dpld->dpctl->ipc_map,
+ sizeof(struct dpram_ipc_map));
+ } else {
+ if (dpld->dp_size == DPRAM_SIZE_16KB)
+ dpram_remap_std_16k_region(dpld);
+ else
+ return -EINVAL;
+ }
+
+ dpld->magic = dpld->ipc_map.magic;
+ dpld->access = dpld->ipc_map.access;
+ for (i = 0; i < dpld->max_ipc_dev; i++)
+ dpld->dev[i] = &dpld->ipc_map.dev[i];
+ dpld->mbx2ap = dpld->ipc_map.mbx_cp2ap;
+ dpld->mbx2cp = dpld->ipc_map.mbx_ap2cp;
+
/* Map for booting */
- if (ld->mdm_data->modem_type == SEC_CMC221) {
- dpld->bt_map.buff = (u8 *)(dp_base);
- dpld->bt_map.req = (u32 *)(dp_base + DP_BOOT_REQ_OFFSET);
- dpld->bt_map.resp = (u32 *)(dp_base + DP_BOOT_RESP_OFFSET);
- dpld->bt_map.size = dpld->dp_size;
- } else if (ld->mdm_data->modem_type == QC_MDM6600) {
- if (dpld->dpctl->bt_map_init)
- dpld->dpctl->bt_map_init(dpld->dpctl);
- } else if (ld->mdm_data->modem_type == VIA_CBP72) {
+ if (dpld->ext_op && dpld->ext_op->init_boot_map) {
+ dpld->ext_op->init_boot_map(dpld);
+ } else {
dpld->bt_map.magic = (u32 *)(dp_base);
dpld->bt_map.buff = (u8 *)(dp_base + DP_BOOT_BUFF_OFFSET);
- dpld->bt_map.size = dpld->dp_size - 4;
- } else {
- dpld->bt_map.buff = (u8 *)(dp_base);
- dpld->bt_map.req = (u32 *)(dp_base + DP_BOOT_REQ_OFFSET);
- dpld->bt_map.resp = (u32 *)(dp_base + DP_BOOT_RESP_OFFSET);
- dpld->bt_map.size = dpld->dp_size - 4;
+ dpld->bt_map.size = dpld->dp_size - 8;
}
/* Map for download (FOTA, UDL, etc.) */
- if (ld->mdm_data->modem_type == SEC_CMC221 ||
- ld->mdm_data->modem_type == VIA_CBP72) {
+ if (dpld->ext_op && dpld->ext_op->init_dl_map) {
+ dpld->ext_op->init_dl_map(dpld);
+ } else {
dpld->dl_map.magic = (u32 *)(dp_base);
- dpld->dl_map.buff = (u8 *)(dp_base + DP_DLOAD_BUFF_OFFSET);
+ dpld->dl_map.buff = (u8 *)(dp_base + DP_DLOAD_BUFF_OFFSET);
}
/* Map for upload mode */
- dpld->ul_map.magic = (u32 *)(dp_base);
- if (ld->mdm_data->modem_type == SEC_CMC221)
- dpld->ul_map.buff = (u8 *)(dp_base);
- else
+ if (dpld->ext_op && dpld->ext_op->init_ul_map) {
+ dpld->ext_op->init_ul_map(dpld);
+ } else {
+ dpld->ul_map.magic = (u32 *)(dp_base);
dpld->ul_map.buff = (u8 *)(dp_base + DP_ULOAD_BUFF_OFFSET);
+ }
+
+ return 0;
}
-#if defined(CONFIG_MACH_M0_CTC)
-static void dpram_link_terminate(struct link_device *ld, struct io_device *iod)
+static void dpram_setup_common_op(struct dpram_link_device *dpld)
{
- struct dpram_link_device *dpld = to_dpram_link_device(ld);
+ dpld->clear_intr = clear_intr;
+ dpld->recv_intr = recv_intr;
+ dpld->send_intr = send_intr;
+ dpld->get_magic = get_magic;
+ dpld->set_magic = set_magic;
+ dpld->get_access = get_access;
+ dpld->set_access = set_access;
+ dpld->get_tx_head = get_tx_head;
+ dpld->get_tx_tail = get_tx_tail;
+ dpld->set_tx_head = set_tx_head;
+ dpld->set_tx_tail = set_tx_tail;
+ dpld->get_tx_buff = get_tx_buff;
+ dpld->get_tx_buff_size = get_tx_buff_size;
+ dpld->get_rx_head = get_rx_head;
+ dpld->get_rx_tail = get_rx_tail;
+ dpld->set_rx_head = set_rx_head;
+ dpld->set_rx_tail = set_rx_tail;
+ dpld->get_rx_buff = get_rx_buff;
+ dpld->get_rx_buff_size = get_rx_buff_size;
+ dpld->get_mask_req_ack = get_mask_req_ack;
+ dpld->get_mask_res_ack = get_mask_res_ack;
+ dpld->get_mask_send = get_mask_send;
+ dpld->ipc_rx_handler = dpram_ipc_rx;
+}
- mif_info("dpram_link_terminate\n");
+static int dpram_link_init(struct link_device *ld, struct io_device *iod)
+{
+ return 0;
+}
- if (dpld->dpctl->terminate_link)
- dpld->dpctl->terminate_link(dpld->dpctl);
+static void dpram_link_terminate(struct link_device *ld, struct io_device *iod)
+{
+ return;
}
-#endif
struct link_device *dpram_create_link_device(struct platform_device *pdev)
{
+ struct modem_data *mdm_data = NULL;
struct dpram_link_device *dpld = NULL;
struct link_device *ld = NULL;
- struct modem_data *pdata = NULL;
+ struct resource *res = NULL;
+ resource_size_t res_size;
struct modemlink_dpram_control *dpctl = NULL;
+ unsigned long task_data;
int ret = 0;
int i = 0;
int bsize;
int qsize;
- char wq_name[32];
- char wq_suffix[32];
/* Get the platform data */
- pdata = (struct modem_data *)pdev->dev.platform_data;
- if (!pdata) {
- mif_info("ERR! pdata == NULL\n");
+ mdm_data = (struct modem_data *)pdev->dev.platform_data;
+ if (!mdm_data) {
+ mif_info("ERR! mdm_data == NULL\n");
goto err;
}
- if (!pdata->dpram_ctl) {
- mif_info("ERR! pdata->dpram_ctl == NULL\n");
+ mif_info("modem = %s\n", mdm_data->name);
+ mif_info("link device = %s\n", mdm_data->link_name);
+
+ if (!mdm_data->dpram_ctl) {
+ mif_info("ERR! mdm_data->dpram_ctl == NULL\n");
goto err;
}
- mif_info("link device = %s\n", pdata->link_name);
- mif_info("modem = %s\n", pdata->name);
+ dpctl = mdm_data->dpram_ctl;
/* Alloc DPRAM link device structure */
dpld = kzalloc(sizeof(struct dpram_link_device), GFP_KERNEL);
@@ -1805,25 +1651,37 @@ struct link_device *dpram_create_link_device(struct platform_device *pdev)
}
ld = &dpld->ld;
- /* Extract modem data and DPRAM control data from the platform data */
- ld->mdm_data = pdata;
- ld->name = pdata->link_name;
- ld->ipc_version = pdata->ipc_version;
+ /* Retrieve modem data and DPRAM control data from the modem data */
+ ld->mdm_data = mdm_data;
+ ld->name = mdm_data->link_name;
+ ld->ipc_version = mdm_data->ipc_version;
- /* Set attributes as a link device */
- ld->aligned = pdata->dpram_ctl->aligned;
- if (ld->aligned)
- mif_info("%s: ld->aligned == TRUE\n", ld->name);
+ /* Retrieve the most basic data for IPC from the modem data */
+ dpld->dpctl = dpctl;
+ dpld->dp_type = dpctl->dp_type;
+ if (mdm_data->ipc_version < SIPC_VER_50) {
+ if (!dpctl->max_ipc_dev) {
+ mif_info("ERR! no max_ipc_dev\n");
+ goto err;
+ }
+
+ ld->aligned = dpctl->aligned;
+ dpld->max_ipc_dev = dpctl->max_ipc_dev;
+ } else {
+ ld->aligned = 1;
+ dpld->max_ipc_dev = MAX_SIPC5_DEV;
+ }
+
+ /* Set attributes as a link device */
+ ld->init_comm = dpram_link_init;
+ ld->terminate_comm = dpram_link_terminate;
ld->send = dpram_send;
ld->force_dump = dpram_force_dump;
- ld->dump_start = dpram_set_ul_magic;
+ ld->dump_start = dpram_dump_start;
ld->dump_update = dpram_dump_update;
- ld->ioctl = dpram_link_ioctl;
+ ld->ioctl = dpram_ioctl;
-#if defined(CONFIG_MACH_M0_CTC)
- ld->terminate_comm = dpram_link_terminate;
-#endif
INIT_LIST_HEAD(&ld->list);
skb_queue_head_init(&ld->sk_fmt_tx_q);
@@ -1833,54 +1691,68 @@ struct link_device *dpram_create_link_device(struct platform_device *pdev)
ld->skb_txq[IPC_RAW] = &ld->sk_raw_tx_q;
ld->skb_txq[IPC_RFS] = &ld->sk_rfs_tx_q;
- /* Set attributes as a dpram link device */
- dpctl = pdata->dpram_ctl;
- dpld->dpctl = dpctl;
+ /* Set up function pointers */
+ dpram_setup_common_op(dpld);
+ dpld->dpram_dump = dpram_dump_memory;
+ dpld->ext_op = dpram_get_ext_op(mdm_data->modem_type);
+ if (dpld->ext_op && dpld->ext_op->ioctl)
+ dpld->ext_ioctl = dpld->ext_op->ioctl;
+
+ /* Retrieve DPRAM resource */
+ if (!dpctl->dp_base) {
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ mif_info("%s: ERR! platform_get_resource fail\n",
+ ld->name);
+ goto err;
+ }
+ res_size = resource_size(res);
+ dpctl->dp_base = ioremap_nocache(res->start, res_size);
+ dpctl->dp_size = res_size;
+ }
dpld->dp_base = dpctl->dp_base;
dpld->dp_size = dpctl->dp_size;
- dpld->dp_type = dpctl->dp_type;
- dpld->max_ipc_dev = dpctl->max_ipc_dev;
+ mif_info("%s: dp_type %d, aligned %d, dp_base 0x%08X, dp_size %d\n",
+ ld->name, dpld->dp_type, ld->aligned, (int)dpld->dp_base,
+ dpld->dp_size);
- dpld->irq = dpctl->dpram_irq;
- if (dpld->irq < 0) {
- mif_info("%s: ERR! failed to get IRQ#\n", ld->name);
+ /* Initialize DPRAM map (physical map -> logical map) */
+ ret = dpram_table_init(dpld);
+ if (ret < 0) {
+ mif_info("%s: ERR! dpram_table_init fail (err %d)\n",
+ ld->name, ret);
goto err;
}
- mif_info("%s: DPRAM IRQ# = %d\n", ld->name, dpld->irq);
- wake_lock_init(&dpld->dpram_wake_lock, WAKE_LOCK_SUSPEND,
- dpctl->dpram_wlock_name);
+ /* Disable IPC */
+ set_magic(dpld, 0);
+ set_access(dpld, 0);
+ dpld->dpram_init_status = DPRAM_INIT_STATE_NONE;
+
+ /* Initialize locks, completions, and bottom halves */
+ snprintf(dpld->wlock_name, DP_MAX_NAME_LEN, "%s_wlock", ld->name);
+ wake_lock_init(&dpld->wlock, WAKE_LOCK_SUSPEND, dpld->wlock_name);
init_completion(&dpld->dpram_init_cmd);
init_completion(&dpld->modem_pif_init_done);
init_completion(&dpld->udl_start_complete);
init_completion(&dpld->udl_cmd_complete);
- init_completion(&dpld->crash_start_complete);
init_completion(&dpld->dump_start_complete);
init_completion(&dpld->dump_recv_done);
- spin_lock_init(&dpld->tx_lock);
-
- tasklet_init(&dpld->rx_tsk, dpram_ipc_rx_task, (unsigned long)dpld);
-
- /* Initialize DPRAM map (physical map -> logical map) */
- dpram_table_init(dpld);
+ task_data = (unsigned long)dpld;
+ tasklet_init(&dpld->rx_tsk, dpram_ipc_rx_task, task_data);
-#if 0
- dpld->magic = dpctl->ipc_map->magic;
- dpld->access = dpctl->ipc_map->access;
+ /* Prepare SKB queue head for RX processing */
for (i = 0; i < dpld->max_ipc_dev; i++)
- dpld->dev[i] = &dpctl->ipc_map->dev[i];
- dpld->mbx2ap = dpctl->ipc_map->mbx_cp2ap;
- dpld->mbx2cp = dpctl->ipc_map->mbx_ap2cp;
-#endif
+ skb_queue_head_init(&dpld->skb_rxq[i]);
- /* Prepare rxb queue */
+ /* Prepare RXB queue */
qsize = DPRAM_MAX_RXBQ_SIZE;
for (i = 0; i < dpld->max_ipc_dev; i++) {
- bsize = rxbq_get_page_size(dpctl->get_rx_buff_size(i));
+ bsize = rxbq_get_page_size(get_rx_buff_size(dpld, i));
dpld->rxbq[i].size = qsize;
dpld->rxbq[i].in = 0;
dpld->rxbq[i].out = 0;
@@ -1894,30 +1766,49 @@ struct link_device *dpram_create_link_device(struct platform_device *pdev)
ld->name, get_dev_name(i), bsize, qsize);
}
- /* Prepare a clean buffer */
+ /* Prepare a multi-purpose miscellaneous buffer */
dpld->buff = kzalloc(dpld->dp_size, GFP_KERNEL);
if (!dpld->buff) {
mif_info("%s: ERR! kzalloc dpld->buff fail\n", ld->name);
goto err;
}
- if (ld->mdm_data->modem_type == QC_MDM6600) {
- if (dpctl->load_init)
- dpctl->load_init(dpctl);
+ /* Retrieve DPRAM IRQ GPIO# */
+ dpld->gpio_dpram_int = mdm_data->gpio_dpram_int;
+
+ /* Retrieve DPRAM IRQ# */
+ if (!dpctl->dpram_irq) {
+ dpctl->dpram_irq = platform_get_irq_byname(pdev, "dpram_irq");
+ if (dpctl->dpram_irq < 0) {
+ mif_info("%s: ERR! platform_get_irq_byname fail\n",
+ ld->name);
+ goto err;
+ }
}
+ dpld->irq = dpctl->dpram_irq;
- /* Disable IPC */
- dpctl->set_magic(0);
- dpctl->set_access(0);
- dpld->dpram_init_status = DPRAM_INIT_STATE_NONE;
+ /* Retrieve DPRAM IRQ flags */
+ if (!dpctl->dpram_irq_flags)
+ dpctl->dpram_irq_flags = (IRQF_NO_SUSPEND | IRQF_TRIGGER_LOW);
+ dpld->irq_flags = dpctl->dpram_irq_flags;
/* Register DPRAM interrupt handler */
- ret = dpram_register_isr(dpld->irq, dpram_irq_handler,
- dpctl->dpram_irq_flags, dpctl->dpram_irq_name, ld);
+ snprintf(dpld->irq_name, DP_MAX_NAME_LEN, "%s_irq", ld->name);
+ if (dpld->ext_op && dpld->ext_op->irq_handler)
+ dpld->irq_handler = dpld->ext_op->irq_handler;
+ else if (dpld->dp_type == CP_IDPRAM)
+ dpld->irq_handler = cp_idpram_irq_handler;
+ else if (dpld->dp_type == AP_IDPRAM)
+ dpld->irq_handler = ap_idpram_irq_handler;
+ else
+ dpld->irq_handler = ext_dpram_irq_handler;
+
+ ret = dpram_register_isr(dpld->irq, dpld->irq_handler, dpld->irq_flags,
+ dpld->irq_name, dpld);
if (ret)
goto err;
-
- return ld;
+ else
+ return ld;
err:
if (dpld) {
diff --git a/drivers/misc/modem_if/modem_link_device_dpram.h b/drivers/misc/modem_if/modem_link_device_dpram.h
index d61da83..c651e51 100644
--- a/drivers/misc/modem_if/modem_link_device_dpram.h
+++ b/drivers/misc/modem_if/modem_link_device_dpram.h
@@ -23,44 +23,61 @@
#include "modem_prj.h"
-/* for DPRAM hostboot */
-#define CMC22x_AP_BOOT_DOWN_DONE 0x54329876
-#define CMC22x_CP_REQ_MAIN_BIN 0xA5A5A5A5
-#define CMC22x_CP_REQ_NV_DATA 0x5A5A5A5A
-#define CMC22x_CP_DUMP_MAGIC 0xDEADDEAD
-
-#define CMC22x_HOST_DOWN_START 0x1234
-#define CMC22x_HOST_DOWN_END 0x4321
-#define CMC22x_REG_NV_DOWN_END 0xABCD
-#define CMC22x_CAL_NV_DOWN_END 0xDCBA
-
-#define CMC22x_1ST_BUFF_READY 0xAAAA
-#define CMC22x_2ND_BUFF_READY 0xBBBB
-#define CMC22x_1ST_BUFF_FULL 0x1111
-#define CMC22x_2ND_BUFF_FULL 0x2222
-
-#define CMC22x_CP_RECV_NV_END 0x8888
-#define CMC22x_CP_CAL_OK 0x4F4B
-#define CMC22x_CP_CAL_BAD 0x4552
-#define CMC22x_CP_DUMP_END 0xFADE
-
-#define CMC22x_DUMP_BUFF_SIZE 8192 /* 8 KB */
-#define CMC22x_DUMP_WAIT_TIMEOVER 1 /* 1 ms */
+#define DPRAM_MAGIC_CODE 0xAA
/* interrupt masks.*/
-#define INT_MASK_VALID 0x0080
-#define INT_MASK_CMD 0x0040
-#define INT_VALID(x) ((x) & INT_MASK_VALID)
+#define INT_MASK_VALID 0x0080
+#define INT_MASK_CMD 0x0040
+#define INT_VALID(x) ((x) & INT_MASK_VALID)
#define INT_CMD_VALID(x) ((x) & INT_MASK_CMD)
-#define INT_NON_CMD(x) (INT_MASK_VALID | (x))
-#define INT_CMD(x) (INT_MASK_VALID | INT_MASK_CMD | (x))
+#define INT_NON_CMD(x) (INT_MASK_VALID | (x))
+#define INT_CMD(x) (INT_MASK_VALID | INT_MASK_CMD | (x))
+#define EXT_UDL_MASK 0xF000
+#define EXT_UDL_CMD(x) ((x) & EXT_UDL_MASK)
#define EXT_INT_VALID_MASK 0x8000
#define EXT_CMD_VALID_MASK 0x4000
+#define UDL_CMD_VALID_MASK 0x2000
#define EXT_INT_VALID(x) ((x) & EXT_INT_VALID_MASK)
#define EXT_CMD_VALID(x) ((x) & EXT_CMD_VALID_MASK)
+#define UDL_CMD_VALID(x) ((x) & UDL_CMD_VALID_MASK)
#define INT_EXT_CMD(x) (EXT_INT_VALID_MASK | EXT_CMD_VALID_MASK | (x))
+#define EXT_CMD_MASK(x) ((x) & 0x0FFF)
+#define EXT_CMD_SET_SPEED_LOW 0x0011
+#define EXT_CMD_SET_SPEED_MID 0x0012
+#define EXT_CMD_SET_SPEED_HIGH 0x0013
+
+#define UDL_RESULT_SUCCESS 0x1
+#define UDL_RESULT_FAIL 0x2
+
+#define UDL_CMD_MASK(x) (((x) >> 8) & 0xF)
+#define UDL_CMD_RECV_READY 0x1
+#define UDL_CMD_DL_START_REQ 0x2
+#define UDL_CMD_DL_START_RESP 0x3
+#define UDL_CMD_IMAGE_SEND_REQ 0x4
+#define UDL_CMD_SEND_DONE_RESP 0x5
+#define UDL_CMD_SEND_DONE_REQ 0x6
+#define UDL_CMD_UPDATE_DONE 0x7
+#define UDL_CMD_STATUS_UPDATE 0x8
+#define UDL_CMD_IMAGE_SEND_RESP 0x9
+#define UDL_CMD_EFS_CLEAR_RESP 0xB
+#define UDL_CMD_ALARM_BOOT_OK 0xC
+#define UDL_CMD_ALARM_BOOT_FAIL 0xD
+
+#define CMD_IMG_START_REQ 0x9200
+#define CMD_IMG_SEND_REQ 0x9400
+#define CMD_DL_SEND_DONE_REQ 0x9600
+#define CMD_UL_RECV_RESP 0x9601
+#define CMD_UL_RECV_DONE_RESP 0x9801
+
+/* special interrupt cmd indicating modem boot failure. */
+#define INT_POWERSAFE_FAIL 0xDEAD
+
+#define INT_MASK_REQ_ACK_RFS 0x0400 /* Request RES_ACK_RFS */
+#define INT_MASK_RES_ACK_RFS 0x0200 /* Response of REQ_ACK_RFS */
+#define INT_MASK_SEND_RFS 0x0100 /* Indicate sending RFS data */
+
#define INT_MASK_REQ_ACK_F 0x0020
#define INT_MASK_REQ_ACK_R 0x0010
#define INT_MASK_RES_ACK_F 0x0008
@@ -68,10 +85,6 @@
#define INT_MASK_SEND_F 0x0002
#define INT_MASK_SEND_R 0x0001
-#define INT_MASK_REQ_ACK_RFS 0x0400 /* Request RES_ACK_RFS */
-#define INT_MASK_RES_ACK_RFS 0x0200 /* Response of REQ_ACK_RFS */
-#define INT_MASK_SEND_RFS 0x0100 /* Indicate sending RFS data */
-
#define INT_MASK_RES_ACK_SET \
(INT_MASK_RES_ACK_F | INT_MASK_RES_ACK_R | INT_MASK_RES_ACK_RFS)
@@ -95,59 +108,26 @@
#define INT_CMD_SILENT_NV_REBUILDING 0xE
#define INT_CMD_NORMAL_PWR_OFF 0xF
-#define EXT_CMD_MASK(x) ((x) & 0x3FFF)
-#define EXT_CMD_SET_SPEED_LOW 0x0011
-#define EXT_CMD_SET_SPEED_MID 0x0012
-#define EXT_CMD_SET_SPEED_HIGH 0x0013
-
-/* special interrupt cmd indicating modem boot failure. */
-#define INT_POWERSAFE_FAIL 0xDEAD
-
-#define UDL_CMD_VALID(x) (((x) & 0xA000) == 0xA000)
-#define UDL_RESULT_FAIL 0x2
-#define UDL_RESULT_SUCCESS 0x1
-#define UDL_CMD_MASK(x) (((x) >> 8) & 0xF)
-#define UDL_CMD_RECEIVE_READY 0x1
-#define UDL_CMD_DOWNLOAD_START_REQ 0x2
-#define UDL_CMD_DOWNLOAD_START_RESP 0x3
-#define UDL_CMD_IMAGE_SEND_REQ 0x4
-/* change dpram download flow */
-#define UDL_CMD_IMAGE_SEND_RESP 0x9
-
-#define UDL_CMD_SEND_DONE_RESP 0x5
-#define UDL_CMD_SEND_DONE_REQ 0x6
-#define UDL_CMD_UPDATE_DONE 0x7
-
-#define UDL_CMD_STATUS_UPDATE 0x8
-#define UDL_CMD_EFS_CLEAR_RESP 0xB
-#define UDL_CMD_ALARM_BOOT_OK 0xC
-#define UDL_CMD_ALARM_BOOT_FAIL 0xD
-
-#define CMD_IMG_START_REQ 0x9200
-#define CMD_IMG_SEND_REQ 0x9400
-#define CMD_DL_SEND_DONE_REQ 0x9600
-#define CMD_UL_RECEIVE_RESP 0x9601
-#define CMD_UL_RECEIVE_DONE_RESP 0x9801
-
-#define START_INDEX 0x7F
-#define END_INDEX 0x7E
-
-#define DP_MAGIC_DMDL 0x4445444C
-#define DP_MAGIC_UMDL 0x4445444D
-#define DP_DPRAM_SIZE 0x4000
-#define DP_DEFAULT_WRITE_LEN 8168
-#define DP_DEFAULT_DUMP_LEN 16128
-#define DP_DUMP_HEADER_SIZE 7
-
-#define UDL_TIMEOUT (50 * HZ)
-#define UDL_SEND_TIMEOUT (200 * HZ)
-#define FORCE_CRASH_TIMEOUT (3 * HZ)
-#define DUMP_TIMEOUT (30 * HZ)
-#define DUMP_START_TIMEOUT (100 * HZ)
-
-enum cmc22x_boot_mode {
- CMC22x_BOOT_MODE_NORMAL,
- CMC22x_BOOT_MODE_DUMP,
+#define START_FLAG 0x7F
+#define END_FLAG 0x7E
+
+#define DP_MAGIC_DMDL 0x4445444C
+#define DP_MAGIC_UMDL 0x4445444D
+#define DP_DPRAM_SIZE 0x4000
+#define DP_DEFAULT_WRITE_LEN 8168
+#define DP_DEFAULT_DUMP_LEN 16128
+#define DP_DUMP_HEADER_SIZE 7
+
+#define UDL_TIMEOUT (50 * HZ)
+#define UDL_SEND_TIMEOUT (200 * HZ)
+#define FORCE_CRASH_ACK_TIMEOUT (5 * HZ)
+#define DUMP_TIMEOUT (30 * HZ)
+#define DUMP_START_TIMEOUT (100 * HZ)
+#define DUMP_WAIT_TIMEOUT (HZ >> 10) /* 1/1024 second */
+
+enum host_boot_mode {
+ HOST_BOOT_MODE_NORMAL,
+ HOST_BOOT_MODE_DUMP,
};
enum dpram_init_status {
@@ -158,40 +138,27 @@ enum dpram_init_status {
struct dpram_boot_img {
char *addr;
int size;
- enum cmc22x_boot_mode mode;
+ enum host_boot_mode mode;
unsigned req;
unsigned resp;
};
#define MAX_PAYLOAD_SIZE 0x2000
struct dpram_boot_frame {
- unsigned request; /* AP to CP Message */
- unsigned response; /* CP to AP Response */
- ssize_t len; /* request size*/
- unsigned offset; /* offset to write */
+ unsigned req; /* AP->CP request */
+ unsigned resp; /* response expected by AP */
+ ssize_t len; /* data size in the buffer */
+ unsigned offset; /* offset to write into DPRAM */
char data[MAX_PAYLOAD_SIZE];
};
/* buffer type for modem image */
struct dpram_dump_arg {
- char *buff;
- int buff_size;/* AP->CP: Buffer size */
- unsigned req; /* AP->CP request */
- unsigned resp; /* CP->AP response */
- bool cmd; /* AP->CP command */
-};
-
-struct dpram_firmware {
- char *firmware;
- int size;
- int is_delta;
-};
-enum dpram_link_mode {
- DPRAM_LINK_MODE_INVALID = 0,
- DPRAM_LINK_MODE_IPC,
- DPRAM_LINK_MODE_BOOT,
- DPRAM_LINK_MODE_DLOAD,
- DPRAM_LINK_MODE_ULOAD,
+ char *buff; /* pointer to the buffer */
+ int buff_size; /* buffer size */
+ unsigned req; /* AP->CP request */
+ unsigned resp; /* CP->AP response */
+ bool cmd; /* AP->CP command */
};
struct dpram_boot_map {
@@ -202,6 +169,13 @@ struct dpram_boot_map {
u32 size;
};
+struct qc_dpram_boot_map {
+ u8 __iomem *buff;
+ u16 __iomem *frame_size;
+ u16 __iomem *tag;
+ u16 __iomem *count;
+};
+
struct dpram_dload_map {
u32 __iomem *magic;
u8 __iomem *buff;
@@ -212,20 +186,54 @@ struct dpram_uload_map {
u8 __iomem *buff;
};
-struct dpram_ota_header {
- u8 start_index;
- u16 nframes;
- u16 curframe;
- u16 len;
-
-} __packed;
-
struct ul_header {
u8 bop;
u16 total_frame;
u16 curr_frame;
u16 len;
} __packed;
+
+struct dpram_udl_param {
+ unsigned char *addr;
+ unsigned int size;
+ unsigned int count;
+ unsigned int tag;
+};
+
+struct dpram_udl_check {
+ unsigned int total_size;
+ unsigned int rest_size;
+ unsigned int send_size;
+ unsigned int copy_start;
+ unsigned int copy_complete;
+ unsigned int boot_complete;
+};
+
+#define DP_BOOT_BUFF_OFFSET 4
+#define DP_DLOAD_BUFF_OFFSET 4
+#define DP_ULOAD_BUFF_OFFSET 4
+#define DP_BOOT_REQ_OFFSET 0
+#define DP_BOOT_RESP_OFFSET 8
+
+#define MAX_WQ_NAME_LENGTH 64
+
+#define DPRAM_MAX_RXBQ_SIZE 256
+
+struct dpram_rxb {
+ u8 *buff;
+ unsigned size;
+
+ u8 *data;
+ unsigned len;
+};
+
+struct dpram_rxb_queue {
+ int size;
+ int in;
+ int out;
+ struct dpram_rxb *rxb;
+};
+
/*
magic_code +
access_enable +
@@ -245,89 +253,72 @@ struct ul_header {
2
= 16384
*/
+#define DP_16K_FMT_TX_BUFF_SZ 1336
+#define DP_16K_RAW_TX_BUFF_SZ 4564
+#define DP_16K_FMT_RX_BUFF_SZ 1336
+#define DP_16K_RAW_RX_BUFF_SZ 9124
-#define SIPC5_DP_FMT_TX_BUFF_SZ 1336
-#define SIPC5_DP_RAW_TX_BUFF_SZ 4564
-#define SIPC5_DP_FMT_RX_BUFF_SZ 1336
-#define SIPC5_DP_RAW_RX_BUFF_SZ 9124
-
-struct sipc5_dpram_ipc_cfg {
+struct dpram_ipc_16k_map {
u16 magic;
u16 access;
u16 fmt_tx_head;
u16 fmt_tx_tail;
- u8 fmt_tx_buff[SIPC5_DP_FMT_TX_BUFF_SZ];
+ u8 fmt_tx_buff[DP_16K_FMT_TX_BUFF_SZ];
u16 raw_tx_head;
u16 raw_tx_tail;
- u8 raw_tx_buff[SIPC5_DP_RAW_TX_BUFF_SZ];
+ u8 raw_tx_buff[DP_16K_RAW_TX_BUFF_SZ];
u16 fmt_rx_head;
u16 fmt_rx_tail;
- u8 fmt_rx_buff[SIPC5_DP_FMT_RX_BUFF_SZ];
+ u8 fmt_rx_buff[DP_16K_FMT_RX_BUFF_SZ];
u16 raw_rx_head;
u16 raw_rx_tail;
- u8 raw_rx_buff[SIPC5_DP_RAW_RX_BUFF_SZ];
+ u8 raw_rx_buff[DP_16K_RAW_RX_BUFF_SZ];
u16 mbx_cp2ap;
u16 mbx_ap2cp;
};
-#define DPRAM_MAX_SKB_SIZE 3072 /* 3 KB */
+#define DP_MAX_NAME_LEN 32
-#define DP_BOOT_REQ_OFFSET 0
-#define DP_BOOT_BUFF_OFFSET 4
-#define DP_BOOT_RESP_OFFSET 8
-#define DP_DLOAD_BUFF_OFFSET 4
-#define DP_ULOAD_BUFF_OFFSET 4
-
-#define MAX_WQ_NAME_LENGTH 64
-
-#define DPRAM_MAX_RXBQ_SIZE 256
-
-struct dpram_rxb {
- u8 *buff;
- unsigned size;
-
- u8 *data;
- unsigned len;
-};
-
-struct dpram_rxb_queue {
- int size;
- int in;
- int out;
- struct dpram_rxb *rxb;
-};
+struct dpram_ext_op;
struct dpram_link_device {
struct link_device ld;
- /* The mode of this DPRAM link device */
- enum dpram_link_mode mode;
-
/* DPRAM address and size */
- u32 dp_size; /* DPRAM size */
u8 __iomem *dp_base; /* DPRAM base virtual address */
+ u32 dp_size; /* DPRAM size */
enum dpram_type dp_type; /* DPRAM type */
+ /* DPRAM IRQ GPIO# */
+ unsigned gpio_dpram_int;
+
/* DPRAM IRQ from CP */
int irq;
+ unsigned long irq_flags;
+ char irq_name[DP_MAX_NAME_LEN];
/* Link to DPRAM control functions dependent on each platform */
int max_ipc_dev;
struct modemlink_dpram_control *dpctl;
/* Physical configuration -> logical configuration */
- struct dpram_boot_map bt_map;
+ union {
+ struct dpram_boot_map bt_map;
+ struct qc_dpram_boot_map qc_bt_map;
+ };
+
struct dpram_dload_map dl_map;
struct dpram_uload_map ul_map;
/* IPC device map */
- struct dpram_ipc_map *ipc_map;
+ struct dpram_ipc_map ipc_map;
+ /* Pointers (aliases) to IPC device map */
u16 __iomem *magic;
u16 __iomem *access;
struct dpram_ipc_device *dev[MAX_IPC_DEV];
@@ -335,45 +326,116 @@ struct dpram_link_device {
u16 __iomem *mbx2cp;
/* Wakelock for DPRAM device */
- struct wake_lock dpram_wake_lock;
+ struct wake_lock wlock;
+ char wlock_name[DP_MAX_NAME_LEN];
/* For booting */
+ unsigned boot_start_complete;
struct completion dpram_init_cmd;
struct completion modem_pif_init_done;
/* For UDL */
+ struct tasklet_struct ul_tsk;
+ struct tasklet_struct dl_tsk;
struct completion udl_start_complete;
struct completion udl_cmd_complete;
+ struct dpram_udl_check udl_check;
+ struct dpram_udl_param udl_param;
/* For CP RAM dump */
- struct completion crash_start_complete;
+ struct timer_list crash_ack_timer;
struct completion dump_start_complete;
struct completion dump_recv_done;
struct timer_list dump_timer;
int dump_rcvd; /* Count of dump packets received */
- /* For locking Tx process */
- spinlock_t tx_lock;
+ /* For locking TX process */
+ spinlock_t tx_lock[MAX_IPC_DEV];
/* For efficient RX process */
struct tasklet_struct rx_tsk;
struct dpram_rxb_queue rxbq[MAX_IPC_DEV];
-
- /* For wake-up/sleep control */
- atomic_t accessing;
+ struct io_device *iod[MAX_IPC_DEV];
+ bool use_skb;
+ struct sk_buff_head skb_rxq[MAX_IPC_DEV];
/* For retransmission after buffer full state */
atomic_t res_required[MAX_IPC_DEV];
+ /* For wake-up/sleep control */
+ atomic_t accessing;
+
/* Multi-purpose miscellaneous buffer */
u8 *buff;
/* DPRAM IPC initialization status */
int dpram_init_status;
+
+ /* Alias to device-specific IOCTL function */
+ int (*ext_ioctl)(struct dpram_link_device *dpld, struct io_device *iod,
+ unsigned int cmd, unsigned long arg);
+
+ /* Alias to DPRAM IRQ handler */
+ irqreturn_t (*irq_handler)(int irq, void *data);
+
+ /* For DPRAM dump */
+ void (*dpram_dump)(struct link_device *ld, char *buff);
+
+ /* Common operations for each DPRAM */
+ void (*clear_intr)(struct dpram_link_device *dpld);
+ u16 (*recv_intr)(struct dpram_link_device *dpld);
+ void (*send_intr)(struct dpram_link_device *dpld, u16 mask);
+ u16 (*get_magic)(struct dpram_link_device *dpld);
+ void (*set_magic)(struct dpram_link_device *dpld, u16 value);
+ u16 (*get_access)(struct dpram_link_device *dpld);
+ void (*set_access)(struct dpram_link_device *dpld, u16 value);
+ u32 (*get_tx_head)(struct dpram_link_device *dpld, int id);
+ u32 (*get_tx_tail)(struct dpram_link_device *dpld, int id);
+ void (*set_tx_head)(struct dpram_link_device *dpld, int id, u32 head);
+ void (*set_tx_tail)(struct dpram_link_device *dpld, int id, u32 tail);
+ u8 *(*get_tx_buff)(struct dpram_link_device *dpld, int id);
+ u32 (*get_tx_buff_size)(struct dpram_link_device *dpld, int id);
+ u32 (*get_rx_head)(struct dpram_link_device *dpld, int id);
+ u32 (*get_rx_tail)(struct dpram_link_device *dpld, int id);
+ void (*set_rx_head)(struct dpram_link_device *dpld, int id, u32 head);
+ void (*set_rx_tail)(struct dpram_link_device *dpld, int id, u32 tail);
+ u8 *(*get_rx_buff)(struct dpram_link_device *dpld, int id);
+ u32 (*get_rx_buff_size)(struct dpram_link_device *dpld, int id);
+ u16 (*get_mask_req_ack)(struct dpram_link_device *dpld, int id);
+ u16 (*get_mask_res_ack)(struct dpram_link_device *dpld, int id);
+ u16 (*get_mask_send)(struct dpram_link_device *dpld, int id);
+ void (*ipc_rx_handler)(struct dpram_link_device *dpld, u16 int2ap);
+
+ /* Extended operations for various modems */
+ struct dpram_ext_op *ext_op;
};
/* converts from struct link_device* to struct xxx_link_device* */
#define to_dpram_link_device(linkdev) \
container_of(linkdev, struct dpram_link_device, ld)
+struct dpram_ext_op {
+ int exist;
+
+ void (*init_boot_map)(struct dpram_link_device *dpld);
+ void (*init_dl_map)(struct dpram_link_device *dpld);
+ void (*init_ul_map)(struct dpram_link_device *dpld);
+
+ int (*download_binary)(struct dpram_link_device *dpld,
+ struct sk_buff *skb);
+
+ void (*cp_start_handler)(struct dpram_link_device *dpld);
+
+ void (*crash_log)(struct dpram_link_device *dpld);
+ int (*dump_start)(struct dpram_link_device *dpld);
+ int (*dump_update)(struct dpram_link_device *dpld, void *arg);
+
+ int (*ioctl)(struct dpram_link_device *dpld, struct io_device *iod,
+ unsigned int cmd, unsigned long arg);
+
+ irqreturn_t (*irq_handler)(int irq, void *data);
+};
+
+struct dpram_ext_op *dpram_get_ext_op(enum modem_t modem);
+
#endif
diff --git a/drivers/misc/modem_if/modem_link_device_dpram_ext_op.c b/drivers/misc/modem_if/modem_link_device_dpram_ext_op.c
new file mode 100644
index 0000000..1475a46
--- /dev/null
+++ b/drivers/misc/modem_if/modem_link_device_dpram_ext_op.c
@@ -0,0 +1,1175 @@
+/*
+ * Copyright (C) 2010 Samsung Electronics.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/time.h>
+#include <linux/interrupt.h>
+#include <linux/timer.h>
+#include <linux/wakelock.h>
+#include <linux/delay.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/vmalloc.h>
+#include <linux/if_arp.h>
+#include <linux/platform_device.h>
+#include <linux/kallsyms.h>
+#include <linux/platform_data/modem.h>
+
+#include "modem_prj.h"
+#include "modem_link_device_dpram.h"
+#include "modem_utils.h"
+
+#if defined(CONFIG_LTE_MODEM_CMC221)
+/*
+** For host (flashless) booting via DPRAM
+*/
+#define CMC22x_AP_BOOT_DOWN_DONE 0x54329876
+#define CMC22x_CP_REQ_MAIN_BIN 0xA5A5A5A5
+#define CMC22x_CP_REQ_NV_DATA 0x5A5A5A5A
+#define CMC22x_CP_DUMP_MAGIC 0xDEADDEAD
+
+#define CMC22x_HOST_DOWN_START 0x1234
+#define CMC22x_HOST_DOWN_END 0x4321
+#define CMC22x_REG_NV_DOWN_END 0xABCD
+#define CMC22x_CAL_NV_DOWN_END 0xDCBA
+
+#define CMC22x_1ST_BUFF_READY 0xAAAA
+#define CMC22x_2ND_BUFF_READY 0xBBBB
+#define CMC22x_1ST_BUFF_FULL 0x1111
+#define CMC22x_2ND_BUFF_FULL 0x2222
+
+#define CMC22x_CP_RECV_NV_END 0x8888
+#define CMC22x_CP_CAL_OK 0x4F4B
+#define CMC22x_CP_CAL_BAD 0x4552
+#define CMC22x_CP_DUMP_END 0xFADE
+
+#define CMC22x_DUMP_BUFF_SIZE 8192 /* 8 KB */
+#endif
+
+#if defined(CONFIG_CDMA_MODEM_CBP72)
+static void cbp72_init_boot_map(struct dpram_link_device *dpld)
+{
+ struct dpram_boot_map *bt_map = &dpld->bt_map;
+
+ bt_map->magic = (u32 *)dpld->dp_base;
+ bt_map->buff = (u8 *)(dpld->dp_base + DP_BOOT_BUFF_OFFSET);
+ bt_map->size = dpld->dp_size - 4;
+}
+
+static void cbp72_init_dl_map(struct dpram_link_device *dpld)
+{
+ dpld->dl_map.magic = (u32 *)dpld->dp_base;
+ dpld->dl_map.buff = (u8 *)(dpld->dp_base + DP_DLOAD_BUFF_OFFSET);
+}
+
+static int _cbp72_edpram_wait_resp(struct dpram_link_device *dpld, u32 resp)
+{
+ struct link_device *ld = &dpld->ld;
+ int ret;
+ int int2cp;
+
+ ret = wait_for_completion_interruptible_timeout(
+ &dpld->udl_cmd_complete, UDL_TIMEOUT);
+ if (!ret) {
+ mif_info("%s: ERR! No UDL_CMD_RESP!!!\n", ld->name);
+ return -ENXIO;
+ }
+
+ int2cp = dpld->recv_intr(dpld);
+ mif_debug("%s: int2cp = 0x%x\n", ld->name, int2cp);
+ if (resp == int2cp || int2cp == 0xA700)
+ return int2cp;
+ else
+ return -EINVAL;
+}
+
+static int _cbp72_edpram_download_bin(struct dpram_link_device *dpld,
+ struct sk_buff *skb)
+{
+ struct link_device *ld = &dpld->ld;
+ struct dpram_boot_frame *bf = (struct dpram_boot_frame *)skb->data;
+ u8 __iomem *buff = dpld->bt_map.buff;
+ int err = 0;
+
+ if (bf->len > dpld->bt_map.size) {
+ mif_info("%s: ERR! Out of DPRAM boundary\n", ld->name);
+ err = -EINVAL;
+ goto exit;
+ }
+
+ if (bf->len)
+ memcpy(buff, bf->data, bf->len);
+
+ init_completion(&dpld->udl_cmd_complete);
+
+ if (bf->req)
+ dpld->send_intr(dpld, (u16)bf->req);
+
+ if (bf->resp) {
+ err = _cbp72_edpram_wait_resp(dpld, bf->resp);
+ if (err < 0) {
+ mif_info("%s: ERR! wait_response fail (%d)\n",
+ ld->name, err);
+ goto exit;
+ } else if (err == bf->resp) {
+ err = skb->len;
+ }
+ }
+
+exit:
+ dev_kfree_skb_any(skb);
+ return err;
+}
+
+static int cbp72_download_binary(struct dpram_link_device *dpld,
+ struct sk_buff *skb)
+{
+ if (dpld->dp_type == EXT_DPRAM)
+ return _cbp72_edpram_download_bin(dpld, skb);
+ else
+ return -ENODEV;
+}
+
+static int cbp72_dump_start(struct dpram_link_device *dpld)
+{
+ struct link_device *ld = &dpld->ld;
+ u8 *dest = dpld->ul_map.buff;
+ int ret;
+
+ ld->mode = LINK_MODE_ULOAD;
+
+ ret = del_timer(&dpld->dump_timer);
+ wake_lock(&dpld->wlock);
+
+ iowrite32(DP_MAGIC_UMDL, dpld->ul_map.magic);
+
+ iowrite8((u8)START_FLAG, dest + 0);
+ iowrite8((u8)0x1, dest + 1);
+ iowrite8((u8)0x1, dest + 2);
+ iowrite8((u8)0x0, dest + 3);
+ iowrite8((u8)END_FLAG, dest + 4);
+
+ init_completion(&dpld->dump_start_complete);
+
+ return 0;
+}
+
+static int _cbp72_edpram_upload(struct dpram_link_device *dpld,
+ struct dpram_dump_arg *dump, unsigned char __user *target)
+{
+ struct link_device *ld = &dpld->ld;
+ struct ul_header header;
+ u8 *dest = NULL;
+ u8 *buff = NULL;
+ u16 plen = 0;
+ int err = 0;
+ int ret = 0;
+ int buff_size = 0;
+
+ mif_debug("\n");
+
+ init_completion(&dpld->udl_cmd_complete);
+
+ mif_debug("%s: req %x, resp %x", ld->name, dump->req, dump->resp);
+
+ if (dump->req)
+ dpld->send_intr(dpld, (u16)dump->req);
+
+ if (dump->resp) {
+ err = _cbp72_edpram_wait_resp(dpld, dump->resp);
+ if (err < 0) {
+ mif_info("%s: ERR! wait_response fail (%d)\n",
+ ld->name, err);
+ goto exit;
+ }
+ }
+
+ if (dump->cmd)
+ return err;
+
+ dest = (u8 *)dpld->ul_map.buff;
+
+ header.bop = *(u8 *)(dest);
+ header.total_frame = *(u16 *)(dest + 1);
+ header.curr_frame = *(u16 *)(dest + 3);
+ header.len = *(u16 *)(dest + 5);
+
+ mif_debug("%s: total frame:%d, current frame:%d, data len:%d\n",
+ ld->name, header.total_frame, header.curr_frame, header.len);
+
+ plen = min_t(u16, header.len, DP_DEFAULT_DUMP_LEN);
+
+ buff = vmalloc(DP_DEFAULT_DUMP_LEN);
+ if (!buff) {
+ err = -ENOMEM;
+ goto exit;
+ }
+
+ memcpy(buff, dest + sizeof(struct ul_header), plen);
+ ret = copy_to_user(dump->buff, buff, plen);
+ if (ret < 0) {
+ mif_info("%s: ERR! dump copy_to_user fail\n", ld->name);
+ err = -EIO;
+ goto exit;
+ }
+ buff_size = plen;
+
+ ret = copy_to_user(target + 4, &buff_size, sizeof(int));
+ if (ret < 0) {
+ mif_info("%s: ERR! size copy_to_user fail\n", ld->name);
+ err = -EIO;
+ goto exit;
+ }
+
+ vfree(buff);
+ return err;
+
+exit:
+ if (buff)
+ vfree(buff);
+ iowrite32(0, dpld->ul_map.magic);
+ wake_unlock(&dpld->wlock);
+ return err;
+}
+
+static int cbp72_dump_update(struct dpram_link_device *dpld, void *arg)
+{
+ struct link_device *ld = &dpld->ld;
+ struct dpram_dump_arg dump;
+ int ret;
+
+ ret = copy_from_user(&dump, (void __user *)arg, sizeof(dump));
+ if (ret < 0) {
+ mif_info("%s: ERR! copy_from_user fail\n", ld->name);
+ return ret;
+ }
+
+ return _cbp72_edpram_upload(dpld, &dump, (unsigned char __user *)arg);
+}
+
+static int cbp72_set_dl_magic(struct link_device *ld, struct io_device *iod)
+{
+ struct dpram_link_device *dpld = to_dpram_link_device(ld);
+
+ ld->mode = LINK_MODE_DLOAD;
+
+ iowrite32(DP_MAGIC_DMDL, dpld->dl_map.magic);
+
+ return 0;
+}
+
+static int cbp72_ioctl(struct dpram_link_device *dpld, struct io_device *iod,
+ unsigned int cmd, unsigned long arg)
+{
+ struct link_device *ld = &dpld->ld;
+ int err = 0;
+
+ switch (cmd) {
+ case IOCTL_MODEM_DL_START:
+ err = cbp72_set_dl_magic(ld, iod);
+ if (err < 0)
+ mif_err("%s: ERR! set_dl_magic fail\n", ld->name);
+ break;
+
+ default:
+ mif_err("%s: ERR! invalid cmd 0x%08X\n", ld->name, cmd);
+ err = -EINVAL;
+ break;
+ }
+
+ return err;
+}
+#endif
+
+#if defined(CONFIG_LTE_MODEM_CMC221)
+static void cmc221_init_boot_map(struct dpram_link_device *dpld)
+{
+ struct dpram_boot_map *bt_map = &dpld->bt_map;
+
+ bt_map->buff = dpld->dp_base;
+ bt_map->size = dpld->dp_size;
+ bt_map->req = (u32 *)(dpld->dp_base + DP_BOOT_REQ_OFFSET);
+ bt_map->resp = (u32 *)(dpld->dp_base + DP_BOOT_RESP_OFFSET);
+}
+
+static void cmc221_init_dl_map(struct dpram_link_device *dpld)
+{
+ dpld->dl_map.magic = (u32 *)dpld->dp_base;
+ dpld->dl_map.buff = (u8 *)dpld->dp_base;
+}
+
+static void cmc221_init_ul_map(struct dpram_link_device *dpld)
+{
+ dpld->ul_map.magic = (u32 *)dpld->dp_base;
+ dpld->ul_map.buff = (u8 *)dpld->dp_base;
+}
+
+static int _cmc221_idpram_wait_resp(struct dpram_link_device *dpld, u32 resp)
+{
+ struct link_device *ld = &dpld->ld;
+ int count = 50000;
+ u32 rcvd = 0;
+
+ if (resp == CMC22x_CP_REQ_NV_DATA) {
+ while (1) {
+ rcvd = ioread32(dpld->bt_map.resp);
+ if (rcvd == resp)
+ break;
+
+ rcvd = dpld->dpctl->recv_msg();
+ if (rcvd == 0x9999) {
+ mif_info("%s: Invalid resp 0x%04X\n",
+ ld->name, rcvd);
+ panic("CP Crash ... BAD CRC in CP");
+ }
+
+ if (count-- < 0) {
+ mif_info("%s: Invalid resp 0x%08X\n",
+ ld->name, rcvd);
+ return -EAGAIN;
+ }
+
+ udelay(100);
+ }
+ } else {
+ while (1) {
+ rcvd = dpld->dpctl->recv_msg();
+
+ if (rcvd == resp)
+ break;
+
+ if (resp == CMC22x_CP_RECV_NV_END &&
+ rcvd == CMC22x_CP_CAL_BAD) {
+ mif_info("%s: CMC22x_CP_CAL_BAD\n", ld->name);
+ break;
+ }
+
+ if (count-- < 0) {
+ mif_info("%s: Invalid resp 0x%04X\n",
+ ld->name, rcvd);
+ return -EAGAIN;
+ }
+
+ udelay(100);
+ }
+ }
+
+ return rcvd;
+}
+
+static int _cmc221_idpram_send_boot(struct dpram_link_device *dpld, void *arg)
+{
+ struct link_device *ld = &dpld->ld;
+ u8 __iomem *bt_buff = dpld->bt_map.buff;
+ struct dpram_boot_img cp_img;
+ u8 *img_buff = NULL;
+ int err = 0;
+ int cnt = 0;
+
+ ld->mode = LINK_MODE_BOOT;
+
+ dpld->dpctl->setup_speed(DPRAM_SPEED_LOW);
+
+ /* Test memory... After testing, memory is cleared. */
+ if (mif_test_dpram(ld->name, bt_buff, dpld->bt_map.size) < 0) {
+ mif_info("%s: ERR! mif_test_dpram fail!\n", ld->name);
+ ld->mode = LINK_MODE_OFFLINE;
+ return -EIO;
+ }
+
+ memset(&cp_img, 0, sizeof(struct dpram_boot_img));
+
+ /* Get information about the boot image */
+ err = copy_from_user(&cp_img, arg, sizeof(cp_img));
+ mif_info("%s: CP image addr = 0x%08X, size = %d\n",
+ ld->name, (int)cp_img.addr, cp_img.size);
+
+ /* Alloc a buffer for the boot image */
+ img_buff = kzalloc(dpld->bt_map.size, GFP_KERNEL);
+ if (!img_buff) {
+ mif_info("%s: ERR! kzalloc fail\n", ld->name);
+ ld->mode = LINK_MODE_OFFLINE;
+ return -ENOMEM;
+ }
+
+ /* Copy boot image from the user space to the image buffer */
+ err = copy_from_user(img_buff, cp_img.addr, cp_img.size);
+
+ /* Copy boot image to DPRAM and verify it */
+ memcpy(bt_buff, img_buff, cp_img.size);
+ if (memcmp16_to_io(bt_buff, img_buff, cp_img.size)) {
+ mif_info("%s: ERR! Boot may be broken!!!\n", ld->name);
+ goto err;
+ }
+
+ dpld->dpctl->reset();
+ usleep_range(1000, 2000);
+
+ if (cp_img.mode == HOST_BOOT_MODE_NORMAL) {
+ mif_info("%s: HOST_BOOT_MODE_NORMAL\n", ld->name);
+ mif_info("%s: Send req 0x%08X\n", ld->name, cp_img.req);
+ iowrite32(cp_img.req, dpld->bt_map.req);
+
+ /* Wait for cp_img.resp for up to 2 seconds */
+ mif_info("%s: Wait resp 0x%08X\n", ld->name, cp_img.resp);
+ while (ioread32(dpld->bt_map.resp) != cp_img.resp) {
+ cnt++;
+ usleep_range(1000, 2000);
+
+ if (cnt > 1000) {
+ mif_info("%s: ERR! Invalid resp 0x%08X\n",
+ ld->name, ioread32(dpld->bt_map.resp));
+ goto err;
+ }
+ }
+ } else {
+ mif_info("%s: HOST_BOOT_MODE_DUMP\n", ld->name);
+ }
+
+ kfree(img_buff);
+
+ mif_info("%s: Send BOOT done\n", ld->name);
+
+ dpld->dpctl->setup_speed(DPRAM_SPEED_HIGH);
+
+ return 0;
+
+err:
+ ld->mode = LINK_MODE_OFFLINE;
+ kfree(img_buff);
+
+ mif_info("%s: ERR! Boot send fail!!!\n", ld->name);
+ return -EIO;
+}
+
+static int cmc221_download_boot(struct dpram_link_device *dpld, void *arg)
+{
+ if (dpld->dp_type == CP_IDPRAM)
+ return _cmc221_idpram_send_boot(dpld, arg);
+ else
+ return -ENODEV;
+}
+
+static int _cmc221_idpram_download_bin(struct dpram_link_device *dpld,
+ struct sk_buff *skb)
+{
+ int err = 0;
+ int ret = 0;
+ struct link_device *ld = &dpld->ld;
+ struct dpram_boot_frame *bf = (struct dpram_boot_frame *)skb->data;
+ u8 __iomem *buff = (dpld->bt_map.buff + bf->offset);
+
+ if ((bf->offset + bf->len) > dpld->bt_map.size) {
+ mif_info("%s: ERR! Out of DPRAM boundary\n", ld->name);
+ err = -EINVAL;
+ goto exit;
+ }
+
+ if (bf->len)
+ memcpy(buff, bf->data, bf->len);
+
+ if (bf->req)
+ dpld->dpctl->send_msg((u16)bf->req);
+
+ if (bf->resp) {
+ err = _cmc221_idpram_wait_resp(dpld, bf->resp);
+ if (err < 0)
+ mif_info("%s: ERR! wait_response fail (err %d)\n",
+ ld->name, err);
+ }
+
+ if (bf->req == CMC22x_CAL_NV_DOWN_END)
+ mif_info("%s: CMC22x_CAL_NV_DOWN_END\n", ld->name);
+
+exit:
+ if (err < 0)
+ ret = err;
+ else
+ ret = skb->len;
+
+ dev_kfree_skb_any(skb);
+
+ return ret;
+}
+
+static int cmc221_download_binary(struct dpram_link_device *dpld,
+ struct sk_buff *skb)
+{
+ if (dpld->dp_type == CP_IDPRAM)
+ return _cmc221_idpram_download_bin(dpld, skb);
+ else
+ return -ENODEV;
+}
+
+static int cmc221_dump_start(struct dpram_link_device *dpld)
+{
+ struct link_device *ld = &dpld->ld;
+ int ret;
+
+ ld->mode = LINK_MODE_ULOAD;
+
+ ret = del_timer(&dpld->dump_timer);
+ wake_lock(&dpld->wlock);
+
+ dpld->dump_rcvd = 0;
+ iowrite32(CMC22x_CP_DUMP_MAGIC, dpld->ul_map.magic);
+
+ init_completion(&dpld->dump_start_complete);
+
+ return 0;
+}
+
+static void _cmc221_idpram_wait_dump(unsigned long arg)
+{
+ struct dpram_link_device *dpld = (struct dpram_link_device *)arg;
+ u16 msg;
+
+ msg = dpld->dpctl->recv_msg();
+ if (msg == CMC22x_CP_DUMP_END) {
+ complete_all(&dpld->dump_recv_done);
+ return;
+ }
+
+ if (((dpld->dump_rcvd & 0x1) == 0) && (msg == CMC22x_1ST_BUFF_FULL)) {
+ complete_all(&dpld->dump_recv_done);
+ return;
+ }
+
+ if (((dpld->dump_rcvd & 0x1) == 1) && (msg == CMC22x_2ND_BUFF_FULL)) {
+ complete_all(&dpld->dump_recv_done);
+ return;
+ }
+
+ mif_add_timer(&dpld->dump_timer, DUMP_WAIT_TIMEOUT,
+ _cmc221_idpram_wait_dump, (unsigned long)dpld);
+}
+
+static int _cmc221_idpram_upload(struct dpram_link_device *dpld,
+ struct dpram_dump_arg *dumparg)
+{
+ struct link_device *ld = &dpld->ld;
+ int ret;
+ u8 __iomem *src;
+ int buff_size = CMC22x_DUMP_BUFF_SIZE;
+
+ if ((dpld->dump_rcvd & 0x1) == 0)
+ dpld->dpctl->send_msg(CMC22x_1ST_BUFF_READY);
+ else
+ dpld->dpctl->send_msg(CMC22x_2ND_BUFF_READY);
+
+ init_completion(&dpld->dump_recv_done);
+
+ mif_add_timer(&dpld->dump_timer, DUMP_WAIT_TIMEOUT,
+ _cmc221_idpram_wait_dump, (unsigned long)dpld);
+
+ ret = wait_for_completion_interruptible_timeout(
+ &dpld->dump_recv_done, DUMP_TIMEOUT);
+ if (!ret) {
+ mif_info("%s: ERR! CP didn't send dump data!!!\n", ld->name);
+ goto err_out;
+ }
+
+ if (dpld->dpctl->recv_msg() == CMC22x_CP_DUMP_END) {
+ mif_info("%s: CMC22x_CP_DUMP_END\n", ld->name);
+ return 0;
+ }
+
+ if ((dpld->dump_rcvd & 0x1) == 0)
+ src = dpld->ul_map.buff;
+ else
+ src = dpld->ul_map.buff + CMC22x_DUMP_BUFF_SIZE;
+
+ memcpy(dpld->buff, src, buff_size);
+
+ ret = copy_to_user(dumparg->buff, dpld->buff, buff_size);
+ if (ret < 0) {
+ mif_info("%s: ERR! copy_to_user fail\n", ld->name);
+ goto err_out;
+ }
+
+ dpld->dump_rcvd++;
+ return buff_size;
+
+err_out:
+ return -EIO;
+}
+
+static int cmc221_dump_update(struct dpram_link_device *dpld, void *arg)
+{
+ struct link_device *ld = &dpld->ld;
+ struct dpram_dump_arg dump;
+ int ret;
+
+ ret = copy_from_user(&dump, (void __user *)arg, sizeof(dump));
+ if (ret < 0) {
+ mif_info("%s: ERR! copy_from_user fail\n", ld->name);
+ return ret;
+ }
+
+ return _cmc221_idpram_upload(dpld, &dump);
+}
+
+static int cmc221_ioctl(struct dpram_link_device *dpld, struct io_device *iod,
+ unsigned int cmd, unsigned long arg)
+{
+ struct link_device *ld = &dpld->ld;
+ int err = 0;
+
+ switch (cmd) {
+ case IOCTL_DPRAM_SEND_BOOT:
+ err = cmc221_download_boot(dpld, (void *)arg);
+ if (err < 0)
+ mif_info("%s: ERR! download_boot fail\n", ld->name);
+ break;
+
+ default:
+ mif_err("%s: ERR! invalid cmd 0x%08X\n", ld->name, cmd);
+ err = -EINVAL;
+ break;
+ }
+
+ return err;
+}
+#endif
+
+#if defined(CONFIG_CDMA_MODEM_MDM6600) || defined(CONFIG_GSM_MODEM_ESC6270)
+enum qc_dload_tag {
+ QC_DLOAD_TAG_NONE = 0,
+ QC_DLOAD_TAG_BIN,
+ QC_DLOAD_TAG_NV,
+ QC_DLOAD_TAG_MAX
+};
+
+static void qc_dload_task(unsigned long data);
+
+static void qc_init_boot_map(struct dpram_link_device *dpld)
+{
+ struct qc_dpram_boot_map *bt_map = &dpld->qc_bt_map;
+ struct modemlink_dpram_control *dpctl = dpld->dpctl;
+
+ bt_map->buff = dpld->dp_base;
+ bt_map->frame_size = (u16 *)(dpld->dp_base + dpctl->boot_size_offset);
+ bt_map->tag = (u16 *)(dpld->dp_base + dpctl->boot_tag_offset);
+ bt_map->count = (u16 *)(dpld->dp_base + dpctl->boot_count_offset);
+
+ tasklet_init(&dpld->dl_tsk, qc_dload_task, (unsigned long)dpld);
+}
+
+static int qc_prepare_download(struct dpram_link_device *dpld)
+{
+ int retval = 0;
+ int count = 0;
+
+ while (1) {
+ if (dpld->udl_check.copy_start) {
+ dpld->udl_check.copy_start = 0;
+ break;
+ }
+
+ msleep_interruptible(10);
+
+ count++;
+ if (count > 200) {
+ mif_err("ERR! count %d\n", count);
+ return -1;
+ }
+ }
+
+ return retval;
+}
+
+static void _qc_do_download(struct dpram_link_device *dpld,
+ struct dpram_udl_param *param)
+{
+ struct qc_dpram_boot_map *bt_map = &dpld->qc_bt_map;
+
+ if (param->size <= dpld->dpctl->max_boot_frame_size) {
+ memcpy(bt_map->buff, param->addr, param->size);
+ iowrite16(param->size, bt_map->frame_size);
+ iowrite16(param->tag, bt_map->tag);
+ iowrite16(param->count, bt_map->count);
+ dpld->send_intr(dpld, 0xDB12);
+ } else {
+ mif_info("param->size %d\n", param->size);
+ }
+}
+
+static int _qc_download(struct dpram_link_device *dpld, void *arg,
+ enum qc_dload_tag tag)
+{
+ int retval = 0;
+ int count = 0;
+ int cnt_limit;
+ unsigned char *img;
+ struct dpram_udl_param param;
+
+ retval = copy_from_user((void *)&param, (void *)arg, sizeof(param));
+ if (retval < 0) {
+ mif_err("ERR! copy_from_user fail\n");
+ return -1;
+ }
+
+ img = vmalloc(param.size);
+ if (!img) {
+ mif_err("ERR! vmalloc fail\n");
+ return -1;
+ }
+ memset(img, 0, param.size);
+ memcpy(img, param.addr, param.size);
+
+ dpld->udl_check.total_size = param.size;
+ dpld->udl_check.rest_size = param.size;
+ dpld->udl_check.send_size = 0;
+ dpld->udl_check.copy_complete = 0;
+
+ dpld->udl_param.addr = img;
+ dpld->udl_param.size = dpld->dpctl->max_boot_frame_size;
+ if (tag == QC_DLOAD_TAG_NV)
+ dpld->udl_param.count = 1;
+ else
+ dpld->udl_param.count = param.count;
+ dpld->udl_param.tag = tag;
+
+ if (dpld->udl_check.rest_size < dpld->dpctl->max_boot_frame_size)
+ dpld->udl_param.size = dpld->udl_check.rest_size;
+
+ /* Download image (binary or NV) */
+ _qc_do_download(dpld, &dpld->udl_param);
+
+ /* Wait for completion
+ */
+ if (tag == QC_DLOAD_TAG_NV)
+ cnt_limit = 200;
+ else
+ cnt_limit = 1000;
+
+ while (1) {
+ if (dpld->udl_check.copy_complete) {
+ dpld->udl_check.copy_complete = 0;
+ retval = 0;
+ break;
+ }
+
+ msleep(10);
+
+ count++;
+ if (count > cnt_limit) {
+ dpld->udl_check.total_size = 0;
+ dpld->udl_check.rest_size = 0;
+ mif_err("ERR! count %d\n", count);
+ retval = -1;
+ break;
+ }
+ }
+
+ vfree(img);
+
+ return retval;
+}
+
+static int qc_download_binary(struct dpram_link_device *dpld, void *arg)
+{
+ return _qc_download(dpld, arg, QC_DLOAD_TAG_BIN);
+}
+
+static int qc_download_nv(struct dpram_link_device *dpld, void *arg)
+{
+ return _qc_download(dpld, arg, QC_DLOAD_TAG_NV);
+}
+
+static void qc_dload_task(unsigned long data)
+{
+ struct dpram_link_device *dpld = (struct dpram_link_device *)data;
+
+ dpld->udl_check.send_size += dpld->udl_param.size;
+ dpld->udl_check.rest_size -= dpld->udl_param.size;
+
+ dpld->udl_param.addr += dpld->udl_param.size;
+
+ if (dpld->udl_check.send_size >= dpld->udl_check.total_size) {
+ dpld->udl_check.copy_complete = 1;
+ dpld->udl_param.tag = 0;
+ return;
+ }
+
+ if (dpld->udl_check.rest_size < dpld->dpctl->max_boot_frame_size)
+ dpld->udl_param.size = dpld->udl_check.rest_size;
+
+ dpld->udl_param.count += 1;
+
+ _qc_do_download(dpld, &dpld->udl_param);
+}
+
+static void qc_dload_cmd_handler(struct dpram_link_device *dpld, u16 cmd)
+{
+ switch (cmd) {
+ case 0x1234:
+ dpld->udl_check.copy_start = 1;
+ break;
+
+ case 0xDBAB:
+ if (dpld->udl_check.total_size)
+ tasklet_schedule(&dpld->dl_tsk);
+ break;
+
+ case 0xABCD:
+ dpld->udl_check.boot_complete = 1;
+ break;
+
+ default:
+ mif_err("ERR! unknown command 0x%04X\n", cmd);
+ }
+}
+
+static int qc_boot_start(struct dpram_link_device *dpld)
+{
+ u16 mask = 0;
+ int count = 0;
+
+ /* Send interrupt -> '0x4567' */
+ mask = 0x4567;
+ dpld->send_intr(dpld, mask);
+
+ while (1) {
+ if (dpld->udl_check.boot_complete) {
+ dpld->udl_check.boot_complete = 0;
+ break;
+ }
+
+ msleep_interruptible(10);
+
+ count++;
+ if (count > 200) {
+ mif_err("ERR! count %d\n", count);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int qc_boot_post_process(struct dpram_link_device *dpld)
+{
+ int count = 0;
+
+ while (1) {
+ if (dpld->boot_start_complete) {
+ dpld->boot_start_complete = 0;
+ break;
+ }
+
+ msleep_interruptible(10);
+
+ count++;
+ if (count > 200) {
+ mif_err("ERR! count %d\n", count);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static void qc_start_handler(struct dpram_link_device *dpld)
+{
+ /*
+ * INT_MASK_VALID | INT_MASK_CMD | INT_MASK_CP_AIRPLANE_BOOT |
+ * INT_MASK_CP_AP_ANDROID | INT_MASK_CMD_INIT_END
+ */
+ u16 mask = (0x0080 | 0x0040 | 0x1000 | 0x0100 | 0x0002);
+
+ dpld->boot_start_complete = 1;
+
+ /* Send INIT_END code to CP */
+ mif_info("send 0x%04X (INIT_END)\n", mask);
+
+ dpld->send_intr(dpld, mask);
+}
+
+static void qc_crash_log(struct dpram_link_device *dpld)
+{
+ struct link_device *ld = &dpld->ld;
+ static unsigned char buf[151];
+ u8 __iomem *data = NULL;
+
+ data = dpld->get_rx_buff(dpld, IPC_FMT);
+ memcpy(buf, data, (sizeof(buf) - 1));
+
+ mif_info("PHONE ERR MSG\t| %s Crash\n", ld->mdm_data->name);
+ mif_info("PHONE ERR MSG\t| %s\n", buf);
+}
+
+static int _qc_data_upload(struct dpram_link_device *dpld,
+ struct dpram_udl_param *param)
+{
+ struct qc_dpram_boot_map *bt_map = &dpld->qc_bt_map;
+ int retval = 0;
+ u16 intval = 0;
+ int count = 0;
+
+ while (1) {
+ if (!gpio_get_value(dpld->gpio_dpram_int)) {
+ intval = dpld->recv_intr(dpld);
+ if (intval == 0xDBAB) {
+ break;
+ } else {
+ mif_err("intr 0x%08x\n", intval);
+ return -1;
+ }
+ }
+
+ msleep_interruptible(1);
+
+ count++;
+ if (count > 200) {
+ mif_err("<%s:%d>\n", __func__, __LINE__);
+ return -1;
+ }
+ }
+
+ param->size = ioread16(bt_map->frame_size);
+ memcpy(param->addr, bt_map->buff, param->size);
+ param->tag = ioread16(bt_map->tag);
+ param->count = ioread16(bt_map->count);
+
+ dpld->send_intr(dpld, 0xDB12);
+
+ return retval;
+}
+
+static int qc_uload_step1(struct dpram_link_device *dpld)
+{
+ int retval = 0;
+ int count = 0;
+ u16 intval = 0;
+ u16 mask = 0;
+
+ mif_info("+---------------------------------------------+\n");
+ mif_info("| UPLOAD PHONE SDRAM |\n");
+ mif_info("+---------------------------------------------+\n");
+
+ while (1) {
+ if (!gpio_get_value(dpld->gpio_dpram_int)) {
+ intval = dpld->recv_intr(dpld);
+ mif_info("intr 0x%04x\n", intval);
+ if (intval == 0x1234) {
+ break;
+ } else {
+ mif_info("ERR! invalid intr\n");
+ return -1;
+ }
+ }
+
+ msleep_interruptible(1);
+
+ count++;
+ if (count > 200) {
+ intval = dpld->recv_intr(dpld);
+ mif_info("count %d, intr 0x%04x\n", count, intval);
+ if (intval == 0x1234)
+ break;
+ return -1;
+ }
+ }
+
+ mask = 0xDEAD;
+ dpld->send_intr(dpld, mask);
+
+ return retval;
+}
+
+static int qc_uload_step2(struct dpram_link_device *dpld, void *arg)
+{
+ int retval = 0;
+ struct dpram_udl_param param;
+
+ retval = copy_from_user((void *)&param, (void *)arg, sizeof(param));
+ if (retval < 0) {
+ mif_err("ERR! copy_from_user fail (err %d)\n", retval);
+ return -1;
+ }
+
+ retval = _qc_data_upload(dpld, &param);
+ if (retval < 0) {
+ mif_err("ERR! _qc_data_upload fail (err %d)\n", retval);
+ return -1;
+ }
+
+ if (!(param.count % 500))
+ mif_info("param->count = %d\n", param.count);
+
+ if (param.tag == 4) {
+ enable_irq(dpld->irq);
+ mif_info("param->tag = %d\n", param.tag);
+ }
+
+ retval = copy_to_user((unsigned long *)arg, &param, sizeof(param));
+ if (retval < 0) {
+ mif_err("ERR! copy_to_user fail (err %d)\n", retval);
+ return -1;
+ }
+
+ return retval;
+}
+
+static int qc_ioctl(struct dpram_link_device *dpld, struct io_device *iod,
+ unsigned int cmd, unsigned long arg)
+{
+ struct link_device *ld = &dpld->ld;
+ int err = 0;
+
+ switch (cmd) {
+ case IOCTL_DPRAM_PHONE_POWON:
+ err = qc_prepare_download(dpld);
+ if (err < 0)
+ mif_info("%s: ERR! prepare_download fail\n", ld->name);
+ break;
+
+ case IOCTL_DPRAM_PHONEIMG_LOAD:
+ err = qc_download_binary(dpld, (void *)arg);
+ if (err < 0)
+ mif_info("%s: ERR! download_binary fail\n", ld->name);
+ break;
+
+ case IOCTL_DPRAM_NVDATA_LOAD:
+ err = qc_download_nv(dpld, (void *)arg);
+ if (err < 0)
+ mif_info("%s: ERR! download_nv fail\n", ld->name);
+ break;
+
+ case IOCTL_DPRAM_PHONE_BOOTSTART:
+ err = qc_boot_start(dpld);
+ if (err < 0) {
+ mif_info("%s: ERR! boot_start fail\n", ld->name);
+ break;
+ }
+
+ err = qc_boot_post_process(dpld);
+ if (err < 0)
+ mif_info("%s: ERR! boot_post_process fail\n", ld->name);
+
+ break;
+
+ case IOCTL_DPRAM_PHONE_UPLOAD_STEP1:
+ disable_irq_nosync(dpld->irq);
+ err = qc_uload_step1(dpld);
+ if (err < 0) {
+ enable_irq(dpld->irq);
+ mif_info("%s: ERR! upload_step1 fail\n", ld->name);
+ }
+ break;
+
+ case IOCTL_DPRAM_PHONE_UPLOAD_STEP2:
+ err = qc_uload_step2(dpld, (void *)arg);
+ if (err < 0) {
+ enable_irq(dpld->irq);
+ mif_info("%s: ERR! upload_step2 fail\n", ld->name);
+ }
+ break;
+
+ default:
+ mif_err("%s: ERR! invalid cmd 0x%08X\n", ld->name, cmd);
+ err = -EINVAL;
+ break;
+ }
+
+ return err;
+}
+
+static irqreturn_t qc_dpram_irq_handler(int irq, void *data)
+{
+ struct dpram_link_device *dpld = (struct dpram_link_device *)data;
+ struct link_device *ld = (struct link_device *)&dpld->ld;
+ u16 int2ap = 0;
+
+ if (unlikely(ld->mode == LINK_MODE_OFFLINE))
+ return IRQ_HANDLED;
+
+ int2ap = dpld->recv_intr(dpld);
+
+ if (int2ap == INT_POWERSAFE_FAIL) {
+ mif_info("%s: int2ap == INT_POWERSAFE_FAIL\n", ld->name);
+ goto exit;
+ }
+
+ if (int2ap == 0x1234 || int2ap == 0xDBAB || int2ap == 0xABCD) {
+ qc_dload_cmd_handler(dpld, int2ap);
+ goto exit;
+ }
+
+ if (likely(INT_VALID(int2ap)))
+ dpld->ipc_rx_handler(dpld, int2ap);
+ else
+ mif_info("%s: ERR! invalid intr 0x%04X\n", ld->name, int2ap);
+
+exit:
+ return IRQ_HANDLED;
+}
+#endif
+
+static struct dpram_ext_op ext_op_set[] = {
+#ifdef CONFIG_CDMA_MODEM_CBP72
+ [VIA_CBP72] = {
+ .exist = 1,
+ .init_boot_map = cbp72_init_boot_map,
+ .init_dl_map = cbp72_init_dl_map,
+ .download_binary = cbp72_download_binary,
+ .dump_start = cbp72_dump_start,
+ .dump_update = cbp72_dump_update,
+ .ioctl = cbp72_ioctl,
+ },
+#endif
+#ifdef CONFIG_LTE_MODEM_CMC221
+ [SEC_CMC221] = {
+ .exist = 1,
+ .init_boot_map = cmc221_init_boot_map,
+ .init_dl_map = cmc221_init_dl_map,
+ .init_ul_map = cmc221_init_ul_map,
+ .download_binary = cmc221_download_binary,
+ .dump_start = cmc221_dump_start,
+ .dump_update = cmc221_dump_update,
+ .ioctl = cmc221_ioctl,
+ },
+#endif
+#if defined(CONFIG_CDMA_MODEM_MDM6600)
+ [QC_MDM6600] = {
+ .exist = 1,
+ .init_boot_map = qc_init_boot_map,
+ .cp_start_handler = qc_start_handler,
+ .crash_log = qc_crash_log,
+ .ioctl = qc_ioctl,
+ .irq_handler = qc_dpram_irq_handler,
+ },
+#endif
+#if defined(CONFIG_GSM_MODEM_ESC6270)
+ [QC_ESC6270] = {
+ .exist = 1,
+ .init_boot_map = qc_init_boot_map,
+ .cp_start_handler = qc_start_handler,
+ .crash_log = qc_crash_log,
+ .ioctl = qc_ioctl,
+ .irq_handler = qc_dpram_irq_handler,
+ },
+#endif
+};
+
+struct dpram_ext_op *dpram_get_ext_op(enum modem_t modem)
+{
+ if (ext_op_set[modem].exist)
+ return &ext_op_set[modem];
+ else
+ return NULL;
+}
+
diff --git a/drivers/misc/modem_if/modem_link_device_hsic.c b/drivers/misc/modem_if/modem_link_device_hsic.c
index af058f4..955c620 100755..100644
--- a/drivers/misc/modem_if/modem_link_device_hsic.c
+++ b/drivers/misc/modem_if/modem_link_device_hsic.c
@@ -47,18 +47,6 @@ static int usb_tx_urb_with_skb(struct usb_device *usbdev, struct sk_buff *skb,
#endif
static void usb_rx_complete(struct urb *urb);
-#if 1
-static void usb_set_autosuspend_delay(struct usb_device *usbdev, int delay)
-{
- pm_runtime_set_autosuspend_delay(&usbdev->dev, delay);
-}
-#else
-static void usb_set_autosuspend_delay(struct usb_device *usbdev, int delay)
-{
- usbdev->autosuspend_delay = msecs_to_jiffies(delay);
-}
-#endif
-
static int start_ipc(struct link_device *ld, struct io_device *iod)
{
struct sk_buff *skb;
@@ -177,7 +165,7 @@ static int usb_rx_submit(struct usb_link_device *usb_ld,
pipe_data->rx_buf_size, usb_rx_complete,
(void *)pipe_data);
- if (!usb_ld->if_usb_connected || !usb_ld->usbdev)
+ if (pipe_data->disconnected)
return -ENOENT;
usb_mark_last_busy(usb_ld->usbdev);
@@ -406,6 +394,8 @@ static void usb_tx_complete(struct urb *urb)
{
struct sk_buff *skb = urb->context;
struct io_device *iod = skbpriv(skb)->iod;
+ struct link_device *ld = skbpriv(skb)->ld;
+ struct usb_link_device *usb_ld = to_usb_link_device(ld);
switch (urb->status) {
case 0:
@@ -419,7 +409,7 @@ static void usb_tx_complete(struct urb *urb)
}
dev_kfree_skb_any(skb);
- if (urb->dev)
+ if (urb->dev && usb_ld->if_usb_connected)
usb_mark_last_busy(urb->dev);
usb_free_urb(urb);
}
@@ -439,14 +429,7 @@ static int usb_tx_urb_with_skb(struct usb_device *usbdev, struct sk_buff *skb,
mif_err("alloc urb error\n");
return -ENOMEM;
}
-#if 0
- int i;
- for (i = 0; i < skb->len; i++) {
- if (i > 16)
- break;
- mif_err("[0x%02x]", *(skb->data + i));
- }
-#endif
+
urb->transfer_flags = URB_ZERO_PACKET;
usb_fill_bulk_urb(urb, pipe_data->usbdev, pipe_data->tx_pipe, skb->data,
skb->len, usb_tx_complete, (void *)skb);
@@ -537,7 +520,7 @@ static void usb_tx_work(struct work_struct *work)
* probing, _usb_tx_work return to -ENOENT then runtime usage
* count allways positive and never enter to L2
*/
- if (!usb_ld->if_usb_connected_last) {
+ if (!usb_ld->if_usb_connected) {
mif_info("link is available, but if was not readey\n");
goto retry_tx_work;
}
@@ -649,7 +632,7 @@ static void link_pm_runtime_start(struct work_struct *work)
struct link_pm_data *pm_data =
container_of(work, struct link_pm_data, link_pm_start.work);
struct usb_device *usbdev = pm_data->usb_ld->usbdev;
- struct device *dev, *ppdev;
+ struct device *dev, *hdev;
struct link_device *ld = &pm_data->usb_ld->ld;
if (!pm_data->usb_ld->if_usb_connected
@@ -671,13 +654,15 @@ static void link_pm_runtime_start(struct work_struct *work)
if (pm_data->usb_ld->usbdev && dev->parent) {
mif_info("rpm_status: %d\n",
dev->power.runtime_status);
- usb_set_autosuspend_delay(usbdev, 200);
- ppdev = dev->parent->parent;
+ pm_runtime_set_autosuspend_delay(dev, 200);
+ hdev = usbdev->bus->root_hub->dev.parent;
+ mif_info("EHCI runtime %s, %s\n", dev_driver_string(hdev),
+ dev_name(hdev));
pm_runtime_allow(dev);
- pm_runtime_allow(ppdev);/*ehci*/
+ pm_runtime_allow(hdev);/*ehci*/
pm_data->link_pm_active = true;
pm_data->resume_requested = false;
- pm_data->link_reconnect_cnt = 2;
+ pm_data->link_reconnect_cnt = 5;
pm_data->resume_retry_cnt = 0;
/* retry prvious link tx q */
@@ -768,12 +753,6 @@ static inline int link_pm_slave_wake(struct link_pm_data *pm_data)
HOSTWAKE_TRIGLEVEL)
mdelay(5);
}
- /* runtime pm goes to active */
- if (!gpio_get_value(pm_data->gpio_link_active)) {
- mif_debug("gpio [H ACTV : %d] set 1\n",
- gpio_get_value(pm_data->gpio_link_active));
- gpio_set_value(pm_data->gpio_link_active, 1);
- }
return spin;
}
@@ -833,6 +812,11 @@ static void link_pm_runtime_work(struct work_struct *work)
}
wake_unlock(&pm_data->rpm_wake);
break;
+ case RPM_SUSPENDING:
+ /* Checking the usb_runtime_suspend running time.*/
+ mif_info("rpm_states=%d", dev->power.runtime_status);
+ msleep(20);
+ break;
default:
break;
}
@@ -919,6 +903,7 @@ static long link_pm_ioctl(struct file *file, unsigned int cmd,
{
int value;
struct link_pm_data *pm_data = file->private_data;
+ struct modem_ctl *mc = if_usb_get_modemctl(pm_data);
mif_info("%x\n", cmd);
@@ -955,6 +940,8 @@ static long link_pm_ioctl(struct file *file, unsigned int cmd,
irq_set_irq_type(pm_data->irq_link_hostwake,
IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING);
}
+ case IOCTL_LINK_GET_PHONEACTIVE:
+ return gpio_get_value(mc->gpio_phone_active);
default:
break;
}
@@ -1001,7 +988,10 @@ static int link_pm_notifier_event(struct notifier_block *this,
pm_data->dpm_suspending = true;
#ifdef CONFIG_UMTS_MODEM_XMM6262
/* set PDA Active High if previous state was LPA */
- gpio_set_value(mc->gpio_pda_active, 1);
+ if (!gpio_get_value(pm_data->gpio_link_active)) {
+ mif_info("PDA active High to LPA suspend spot\n");
+ gpio_set_value(mc->gpio_pda_active, 1);
+ }
#endif
mif_debug("dpm suspending set to true\n");
return NOTIFY_OK;
@@ -1017,6 +1007,14 @@ static int link_pm_notifier_event(struct notifier_block *this,
0);
mif_info("post resume\n");
}
+#ifdef CONFIG_UMTS_MODEM_XMM6262
+ /* LPA to Kernel suspend and User Freezing task fail resume,
+ restore to LPA GPIO states. */
+ if (!gpio_get_value(pm_data->gpio_link_active)) {
+ mif_info("PDA active low to LPA GPIO state\n");
+ gpio_set_value(mc->gpio_pda_active, 0);
+ }
+#endif
mif_debug("dpm suspending set to false\n");
return NOTIFY_OK;
}
@@ -1122,7 +1120,7 @@ static void if_usb_disconnect(struct usb_interface *intf)
{
struct if_usb_devdata *devdata = usb_get_intfdata(intf);
struct link_pm_data *pm_data = devdata->usb_ld->link_pm_data;
- struct device *dev, *ppdev;
+ struct device *dev, *hdev;
struct link_device *ld = &devdata->usb_ld->ld;
mif_info("\n");
@@ -1130,13 +1128,14 @@ static void if_usb_disconnect(struct usb_interface *intf)
if (devdata->disconnected)
return;
+ devdata->usb_ld->if_usb_connected = 0;
+
usb_driver_release_interface(to_usb_driver(intf->dev.driver), intf);
usb_kill_urb(devdata->urb);
- dev = &devdata->usb_ld->usbdev->dev;
- ppdev = dev->parent->parent;
- pm_runtime_forbid(ppdev); /*ehci*/
+ hdev = devdata->usbdev->bus->root_hub->dev.parent;
+ pm_runtime_forbid(hdev); /*ehci*/
mif_info("put dev 0x%p\n", devdata->usbdev);
usb_put_dev(devdata->usbdev);
@@ -1148,8 +1147,6 @@ static void if_usb_disconnect(struct usb_interface *intf)
devdata->state = STATE_SUSPENDED;
pm_data->ipc_debug_cnt = 0;
- devdata->usb_ld->if_usb_connected = 0;
- devdata->usb_ld->if_usb_connected_last = 0;
devdata->usb_ld->suspended = 0;
wake_lock(&pm_data->boot_wake);
@@ -1170,9 +1167,12 @@ static void if_usb_disconnect(struct usb_interface *intf)
if (devdata->usb_ld->ld.com_state != COM_ONLINE) {
cancel_delayed_work(&pm_data->link_reconnect_work);
return;
- } else
+ } else {
+ if (pm_data->ehci_reg_dump)
+ pm_data->ehci_reg_dump(hdev);
schedule_delayed_work(&pm_data->link_reconnect_work,
msecs_to_jiffies(500));
+ }
return;
}
@@ -1206,9 +1206,6 @@ static int if_usb_set_pipe(struct usb_link_device *usb_ld,
return 0;
}
-
-static struct usb_id_info hsic_channel_info;
-
static int __devinit if_usb_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
@@ -1226,12 +1223,25 @@ static int __devinit if_usb_probe(struct usb_interface *intf,
mif_info("usbdev = 0x%p\n", usbdev);
+ pr_debug("%s: Class=%d, SubClass=%d, Protocol=%d\n", __func__,
+ intf->altsetting->desc.bInterfaceClass,
+ intf->altsetting->desc.bInterfaceSubClass,
+ intf->altsetting->desc.bInterfaceProtocol);
+
+ /* if usb disconnected, AP try to reconnect 5 times.
+ * but because if_sub_connected is configured
+ * at the end of if_usb_probe, there was a chance
+ * that swk will be called again during enumeration.
+ * so.. cancel reconnect work_queue in this case. */
+ if (usb_ld->ld.com_state == COM_ONLINE)
+ cancel_delayed_work(&usb_ld->link_pm_data->link_reconnect_work);
+
usb_ld->usbdev = usbdev;
pm_runtime_forbid(&usbdev->dev);
usb_ld->link_pm_data->link_pm_active = false;
usb_ld->link_pm_data->dpm_suspending = false;
usb_ld->link_pm_data->ipc_debug_cnt = 0;
- usb_ld->if_usb_is_main = (info == &hsic_channel_info);
+ usb_ld->if_usb_is_main = (info->intf_id != BOOT_DOWN);
union_hdr = NULL;
/* for WMC-ACM compatibility, WMC-ACM use an end-point for control msg*/
@@ -1314,7 +1324,6 @@ static int __devinit if_usb_probe(struct usb_interface *intf,
usb_ld->devdata[pipe].disconnected = 0;
usb_ld->devdata[pipe].state = STATE_RESUMED;
- usb_ld->if_usb_connected = 1;
usb_ld->suspended = 0;
err = usb_driver_claim_interface(usbdrv, data_intf,
@@ -1345,7 +1354,7 @@ static int __devinit if_usb_probe(struct usb_interface *intf,
link_pm_change_modem_state(usb_ld->link_pm_data, STATE_ONLINE);
if (pipe == IF_USB_CMD_EP || info->intf_id == BOOT_DOWN)
- usb_ld->if_usb_connected_last = 1;
+ usb_ld->if_usb_connected = 1;
mif_info("successfully done\n");
@@ -1369,9 +1378,11 @@ static struct usb_id_info hsic_channel_info = {
};
static struct usb_device_id if_usb_ids[] = {
- {USB_DEVICE(IMC_BOOT_VID, IMC_BOOT_PID),
+ {USB_DEVICE_AND_INTERFACE_INFO(IMC_BOOT_VID, IMC_BOOT_PID,
+ USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, USB_CDC_PROTO_NONE),
.driver_info = (unsigned long)&hsic_boot_down_info,},
- {USB_DEVICE(IMC_MAIN_VID, IMC_MAIN_PID),
+ {USB_DEVICE_AND_INTERFACE_INFO(IMC_MAIN_VID, IMC_MAIN_PID,
+ USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, 1),
.driver_info = (unsigned long)&hsic_channel_info,},
{USB_DEVICE(STE_BOOT_VID, STE_BOOT_PID),
.driver_info = (unsigned long)&hsic_boot_down_info,},
@@ -1446,13 +1457,21 @@ static int usb_link_pm_init(struct usb_link_device *usb_ld, void *data)
struct platform_device *pdev = (struct platform_device *)data;
struct modem_data *pdata =
(struct modem_data *)pdev->dev.platform_data;
- struct modemlink_pm_data *pm_pdata = pdata->link_pm_data;
+ struct modemlink_pm_data *pm_pdata;
struct link_pm_data *pm_data =
kzalloc(sizeof(struct link_pm_data), GFP_KERNEL);
+
+ if (!pdata || !pdata->link_pm_data) {
+ mif_err("platform data is NULL\n");
+ return -EINVAL;
+ }
+ pm_pdata = pdata->link_pm_data;
+
if (!pm_data) {
mif_err("link_pm_data is NULL\n");
return -ENOMEM;
}
+
/* get link pm data from modemcontrol's platform data */
pm_data->gpio_link_active = pm_pdata->gpio_link_active;
pm_data->gpio_link_enable = pm_pdata->gpio_link_enable;
@@ -1461,6 +1480,7 @@ static int usb_link_pm_init(struct usb_link_device *usb_ld, void *data)
pm_data->irq_link_hostwake = gpio_to_irq(pm_data->gpio_link_hostwake);
pm_data->link_ldo_enable = pm_pdata->link_ldo_enable;
pm_data->link_reconnect = pm_pdata->link_reconnect;
+ pm_data->ehci_reg_dump = pm_pdata->ehci_reg_dump;
pm_data->usb_ld = usb_ld;
pm_data->link_pm_active = false;
diff --git a/drivers/misc/modem_if/modem_link_device_hsic.h b/drivers/misc/modem_if/modem_link_device_hsic.h
index 8aec412..604b067 100755..100644
--- a/drivers/misc/modem_if/modem_link_device_hsic.h
+++ b/drivers/misc/modem_if/modem_link_device_hsic.h
@@ -36,6 +36,7 @@ enum {
#define IOCTL_LINK_GET_HOSTWAKE _IO('o', 0x32)
#define IOCTL_LINK_CONNECTED _IO('o', 0x33)
#define IOCTL_LINK_SET_BIAS_CLEAR _IO('o', 0x34)
+#define IOCTL_LINK_GET_PHONEACTIVE _IO('o', 0x35)
/* VID,PID for IMC - XMM6260, XMM6262*/
#define IMC_BOOT_VID 0x058b
@@ -102,6 +103,8 @@ struct link_pm_data {
unsigned ipc_debug_cnt;
unsigned long tx_cnt;
unsigned long rx_cnt;
+
+ void (*ehci_reg_dump)(struct device *);
};
struct if_usb_devdata {
@@ -130,10 +133,6 @@ struct usb_link_device {
unsigned int suspended;
int if_usb_connected;
- /*It is same with if_usb_connected, but we need to check the side-effect
- * from timming changed, it will merge with if_usb_connect variable.*/
- int if_usb_connected_last;
-
bool if_usb_is_main; /* boot,down(false) or main(true) */
/* LINK PM DEVICE DATA */
diff --git a/drivers/misc/modem_if/modem_link_device_pld.c b/drivers/misc/modem_if/modem_link_device_pld.c
new file mode 100644
index 0000000..b68040e
--- /dev/null
+++ b/drivers/misc/modem_if/modem_link_device_pld.c
@@ -0,0 +1,1981 @@
+/*
+ * Copyright (C) 2010 Samsung Electronics.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/time.h>
+#include <linux/interrupt.h>
+#include <linux/timer.h>
+#include <linux/wakelock.h>
+#include <linux/delay.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/vmalloc.h>
+#include <linux/if_arp.h>
+#include <linux/platform_device.h>
+#include <linux/kallsyms.h>
+#include <linux/platform_data/modem.h>
+
+#include "modem_prj.h"
+#include "modem_link_device_pld.h"
+#include "modem_utils.h"
+
+
+/*
+** Function prototypes for basic DPRAM operations
+*/
+static inline void clear_intr(struct dpram_link_device *dpld);
+static inline u16 recv_intr(struct dpram_link_device *dpld);
+static inline void send_intr(struct dpram_link_device *dpld, u16 mask);
+
+static inline u16 get_magic(struct dpram_link_device *dpld);
+static inline void set_magic(struct dpram_link_device *dpld, u16 val);
+static inline u16 get_access(struct dpram_link_device *dpld);
+static inline void set_access(struct dpram_link_device *dpld, u16 val);
+
+static inline u32 get_tx_head(struct dpram_link_device *dpld, int id);
+static inline u32 get_tx_tail(struct dpram_link_device *dpld, int id);
+static inline void set_tx_head(struct dpram_link_device *dpld, int id, u32 in);
+static inline void set_tx_tail(struct dpram_link_device *dpld, int id, u32 out);
+static inline u8 *get_tx_buff(struct dpram_link_device *dpld, int id);
+static inline u32 get_tx_buff_size(struct dpram_link_device *dpld, int id);
+
+static inline u32 get_rx_head(struct dpram_link_device *dpld, int id);
+static inline u32 get_rx_tail(struct dpram_link_device *dpld, int id);
+static inline void set_rx_head(struct dpram_link_device *dpld, int id, u32 in);
+static inline void set_rx_tail(struct dpram_link_device *dpld, int id, u32 out);
+static inline u8 *get_rx_buff(struct dpram_link_device *dpld, int id);
+static inline u32 get_rx_buff_size(struct dpram_link_device *dpld, int id);
+
+static inline u16 get_mask_req_ack(struct dpram_link_device *dpld, int id);
+static inline u16 get_mask_res_ack(struct dpram_link_device *dpld, int id);
+static inline u16 get_mask_send(struct dpram_link_device *dpld, int id);
+
+static inline bool dpram_circ_valid(u32 size, u32 in, u32 out);
+
+static void handle_cp_crash(struct dpram_link_device *dpld);
+static int trigger_force_cp_crash(struct dpram_link_device *dpld);
+
+/*
+** Functions for debugging
+*/
+static inline void log_dpram_status(struct dpram_link_device *dpld)
+{
+ pr_info("mif: %s: {M:0x%X A:%d} {FMT TI:%u TO:%u RI:%u RO:%u} "
+ "{RAW TI:%u TO:%u RI:%u RO:%u} {INT:0x%X}\n",
+ dpld->ld.mc->name,
+ get_magic(dpld), get_access(dpld),
+ get_tx_head(dpld, IPC_FMT), get_tx_tail(dpld, IPC_FMT),
+ get_rx_head(dpld, IPC_FMT), get_rx_tail(dpld, IPC_FMT),
+ get_tx_head(dpld, IPC_RAW), get_tx_tail(dpld, IPC_RAW),
+ get_rx_head(dpld, IPC_RAW), get_rx_tail(dpld, IPC_RAW),
+ recv_intr(dpld));
+}
+
+static void set_dpram_map(struct dpram_link_device *dpld,
+ struct mif_irq_map *map)
+{
+ map->magic = get_magic(dpld);
+ map->access = get_access(dpld);
+
+ map->fmt_tx_in = get_tx_head(dpld, IPC_FMT);
+ map->fmt_tx_out = get_tx_tail(dpld, IPC_FMT);
+ map->fmt_rx_in = get_rx_head(dpld, IPC_FMT);
+ map->fmt_rx_out = get_rx_tail(dpld, IPC_FMT);
+ map->raw_tx_in = get_tx_head(dpld, IPC_RAW);
+ map->raw_tx_out = get_tx_tail(dpld, IPC_RAW);
+ map->raw_rx_in = get_rx_head(dpld, IPC_RAW);
+ map->raw_rx_out = get_rx_tail(dpld, IPC_RAW);
+
+ map->cp2ap = recv_intr(dpld);
+}
+
+/*
+** RXB (DPRAM RX buffer) functions
+*/
+static struct dpram_rxb *rxbq_create_pool(unsigned size, int count)
+{
+ struct dpram_rxb *rxb;
+ u8 *buff;
+ int i;
+
+ rxb = kzalloc(sizeof(struct dpram_rxb) * count, GFP_KERNEL);
+ if (!rxb) {
+ mif_info("ERR! kzalloc rxb fail\n");
+ return NULL;
+ }
+
+ buff = kzalloc((size * count), GFP_KERNEL|GFP_DMA);
+ if (!buff) {
+ mif_info("ERR! kzalloc buff fail\n");
+ kfree(rxb);
+ return NULL;
+ }
+
+ for (i = 0; i < count; i++) {
+ rxb[i].buff = buff;
+ rxb[i].size = size;
+ buff += size;
+ }
+
+ return rxb;
+}
+
+static inline unsigned rxbq_get_page_size(unsigned len)
+{
+ return ((len + PAGE_SIZE - 1) >> PAGE_SHIFT) << PAGE_SHIFT;
+}
+
+static inline bool rxbq_empty(struct dpram_rxb_queue *rxbq)
+{
+ return (rxbq->in == rxbq->out) ? true : false;
+}
+
+static inline int rxbq_free_size(struct dpram_rxb_queue *rxbq)
+{
+ int in = rxbq->in;
+ int out = rxbq->out;
+ int qsize = rxbq->size;
+ return (in < out) ? (out - in - 1) : (qsize + out - in - 1);
+}
+
+static inline struct dpram_rxb *rxbq_get_free_rxb(struct dpram_rxb_queue *rxbq)
+{
+ struct dpram_rxb *rxb = NULL;
+
+ if (likely(rxbq_free_size(rxbq) > 0)) {
+ rxb = &rxbq->rxb[rxbq->in];
+ rxbq->in++;
+ if (rxbq->in >= rxbq->size)
+ rxbq->in -= rxbq->size;
+ rxb->data = rxb->buff;
+ }
+
+ return rxb;
+}
+
+static inline int rxbq_size(struct dpram_rxb_queue *rxbq)
+{
+ int in = rxbq->in;
+ int out = rxbq->out;
+ int qsize = rxbq->size;
+ return (in >= out) ? (in - out) : (qsize - out + in);
+}
+
+static inline struct dpram_rxb *rxbq_get_data_rxb(struct dpram_rxb_queue *rxbq)
+{
+ struct dpram_rxb *rxb = NULL;
+
+ if (likely(!rxbq_empty(rxbq))) {
+ rxb = &rxbq->rxb[rxbq->out];
+ rxbq->out++;
+ if (rxbq->out >= rxbq->size)
+ rxbq->out -= rxbq->size;
+ }
+
+ return rxb;
+}
+
+static inline u8 *rxb_put(struct dpram_rxb *rxb, unsigned len)
+{
+ rxb->len = len;
+ return rxb->data;
+}
+
+static inline void rxb_clear(struct dpram_rxb *rxb)
+{
+ rxb->data = NULL;
+ rxb->len = 0;
+}
+
+/*
+** DPRAM operations
+*/
+static int dpram_register_isr(unsigned irq, irqreturn_t (*isr)(int, void*),
+ unsigned long flag, const char *name,
+ struct dpram_link_device *dpld)
+{
+ int ret = 0;
+
+ ret = request_irq(irq, isr, flag, name, dpld);
+ if (ret) {
+ mif_info("%s: ERR! request_irq fail (err %d)\n", name, ret);
+ return ret;
+ }
+
+ ret = enable_irq_wake(irq);
+ if (ret)
+ mif_info("%s: ERR! enable_irq_wake fail (err %d)\n", name, ret);
+
+ mif_info("%s (#%d) handler registered\n", name, irq);
+
+ return 0;
+}
+
+static inline void clear_intr(struct dpram_link_device *dpld)
+{
+ if (dpld->dpctl->clear_intr)
+ dpld->dpctl->clear_intr();
+}
+
+static inline u16 recv_intr(struct dpram_link_device *dpld)
+{
+ u16 val1 = 0, val2 = 0, cnt = 3;
+ unsigned long int flags;
+
+ spin_lock_irqsave(&dpld->pld_lock, flags);
+
+ do {
+ /* Check head value written */
+ iowrite16(PLD_ADDR_MASK(&dpld->mbx2ap[0]),
+ dpld->address_buffer);
+ val1 = ioread16(dpld->dp_base);
+
+ iowrite16(PLD_ADDR_MASK(&dpld->mbx2ap[0]),
+ dpld->address_buffer);
+ val2 = ioread16(dpld->dp_base);
+
+ if (likely(val1 == val2)) {
+ spin_unlock_irqrestore(&dpld->pld_lock, flags);
+ return val1;
+ }
+
+ mif_err("ERR: intr1(%d) != intr1(%d)\n", val1, val2);
+
+ } while (cnt--);
+
+ spin_unlock_irqrestore(&dpld->pld_lock, flags);
+
+ return val1;
+}
+
+static inline void send_intr(struct dpram_link_device *dpld, u16 mask)
+{
+ int cnt = 3;
+ u32 val = 0;
+ unsigned long int flags;
+
+ spin_lock_irqsave(&dpld->pld_lock, flags);
+
+ iowrite16(PLD_ADDR_MASK(&dpld->mbx2cp[0]),
+ dpld->address_buffer);
+ iowrite16((u16)mask, dpld->dp_base);
+
+ do {
+ /* Check head value written */
+ iowrite16(PLD_ADDR_MASK(&dpld->mbx2cp[0]),
+ dpld->address_buffer);
+ val = ioread16(dpld->dp_base);
+
+ if (likely(val == mask)) {
+ spin_unlock_irqrestore(&dpld->pld_lock, flags);
+ return;
+ }
+
+ mif_err("ERR: intr1(%d) != intr2(%d)\n", val, mask);
+ udelay(100);
+
+ /* Write head value again */
+ iowrite16(PLD_ADDR_MASK(&dpld->mbx2cp[0]),
+ dpld->address_buffer);
+ iowrite16((u16)mask, dpld->dp_base);
+ } while (cnt--);
+
+ spin_unlock_irqrestore(&dpld->pld_lock, flags);
+
+ return;
+}
+
+static inline u16 get_magic(struct dpram_link_device *dpld)
+{
+ u16 val1 = 0, val2 = 0, cnt = 3;
+ unsigned long int flags;
+
+ spin_lock_irqsave(&dpld->pld_lock, flags);
+
+ do {
+ /* Check head value written */
+ iowrite16(PLD_ADDR_MASK(&dpld->magic_ap2cp[0]),
+ dpld->address_buffer);
+ val1 = ioread16(dpld->dp_base);
+
+ iowrite16(PLD_ADDR_MASK(&dpld->magic_ap2cp[0]),
+ dpld->address_buffer);
+ val2 = ioread16(dpld->dp_base);
+
+ if (likely(val1 == val2)) {
+ spin_unlock_irqrestore(&dpld->pld_lock, flags);
+ return val1;
+ }
+
+ mif_err("ERR: txq.head(%d) != in(%d)\n", val1, val2);
+ udelay(100);
+
+ } while (cnt--);
+
+ spin_unlock_irqrestore(&dpld->pld_lock, flags);
+ return val1;
+
+}
+
+static inline void set_magic(struct dpram_link_device *dpld, u16 in)
+{
+ int cnt = 3;
+ u32 val = 0;
+ unsigned long int flags;
+
+ spin_lock_irqsave(&dpld->pld_lock, flags);
+
+ iowrite16(PLD_ADDR_MASK(&dpld->magic_ap2cp[0]),
+ dpld->address_buffer);
+ iowrite16((u16)in, dpld->dp_base);
+
+ do {
+ /* Check head value written */
+ iowrite16(PLD_ADDR_MASK(&dpld->magic_ap2cp[0]),
+ dpld->address_buffer);
+ val = ioread16(dpld->dp_base);
+
+ if (likely(val == in)) {
+ spin_unlock_irqrestore(&dpld->pld_lock, flags);
+ return;
+ }
+
+ mif_err("ERR: magic1(%d) != magic2(%d)\n", val, in);
+ udelay(100);
+
+ /* Write head value again */
+ iowrite16(PLD_ADDR_MASK(&dpld->magic_ap2cp[0]),
+ dpld->address_buffer);
+ iowrite16((u16)in, dpld->dp_base);
+ } while (cnt--);
+
+ spin_unlock_irqrestore(&dpld->pld_lock, flags);
+ return;
+}
+
+static inline u16 get_access(struct dpram_link_device *dpld)
+{
+ u16 val1 = 0, val2 = 0, cnt = 3;
+ unsigned long int flags;
+
+ spin_lock_irqsave(&dpld->pld_lock, flags);
+
+ do {
+ /* Check head value written */
+ iowrite16(PLD_ADDR_MASK(&dpld->access_ap2cp[0]),
+ dpld->address_buffer);
+ val1 = ioread16(dpld->dp_base);
+
+ iowrite16(PLD_ADDR_MASK(&dpld->access_ap2cp[0]),
+ dpld->address_buffer);
+ val2 = ioread16(dpld->dp_base);
+
+ if (likely(val1 == val2)) {
+ spin_unlock_irqrestore(&dpld->pld_lock, flags);
+ return val1;
+ }
+
+ mif_err("ERR: access1(%d) != access2(%d)\n", val1, val2);
+ udelay(100);
+
+ } while (cnt--);
+
+ spin_unlock_irqrestore(&dpld->pld_lock, flags);
+ return val1;
+
+}
+
+static inline void set_access(struct dpram_link_device *dpld, u16 in)
+{
+ int cnt = 3;
+ u32 val = 0;
+ unsigned long int flags;
+
+ iowrite16(PLD_ADDR_MASK(&dpld->access_ap2cp[0]),
+ dpld->address_buffer);
+ iowrite16((u16)in, dpld->dp_base);
+
+ spin_lock_irqsave(&dpld->pld_lock, flags);
+
+ do {
+ /* Check head value written */
+ iowrite16(PLD_ADDR_MASK(&dpld->access_ap2cp[0]),
+ dpld->address_buffer);
+ val = ioread16(dpld->dp_base);
+
+ if (likely(val == in)) {
+ spin_unlock_irqrestore(&dpld->pld_lock, flags);
+ return;
+ }
+
+ mif_err("ERR: access(%d) != access(%d)\n", val, in);
+ udelay(100);
+
+ /* Write head value again */
+ iowrite16(PLD_ADDR_MASK(&dpld->access_ap2cp[0]),
+ dpld->address_buffer);
+ iowrite16((u16)in, dpld->dp_base);
+ } while (cnt--);
+
+ spin_unlock_irqrestore(&dpld->pld_lock, flags);
+ return;
+}
+
+static inline u32 get_tx_head(struct dpram_link_device *dpld, int id)
+{
+ u16 val1 = 0, val2 = 0, cnt = 3;
+ unsigned long int flags;
+
+ spin_lock_irqsave(&dpld->pld_lock, flags);
+
+ do {
+ /* Check head value written */
+ iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->txq.head)[0]),
+ dpld->address_buffer);
+ val1 = ioread16(dpld->dp_base);
+
+ iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->txq.head)[0]),
+ dpld->address_buffer);
+ val2 = ioread16(dpld->dp_base);
+
+ if (likely(val1 == val2)) {
+ spin_unlock_irqrestore(&dpld->pld_lock, flags);
+ return val1;
+ }
+
+ mif_err("ERR: %s txq.head(%d) != in(%d)\n",
+ get_dev_name(id), val1, val2);
+ udelay(100);
+
+ } while (cnt--);
+
+ spin_unlock_irqrestore(&dpld->pld_lock, flags);
+ return val1;
+}
+
+static inline u32 get_tx_tail(struct dpram_link_device *dpld, int id)
+{
+ u16 val1 = 0, val2 = 0, cnt = 3;
+ unsigned long int flags;
+
+ spin_lock_irqsave(&dpld->pld_lock, flags);
+
+ do {
+ /* Check head value written */
+ iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->txq.tail)[0]),
+ dpld->address_buffer);
+ val1 = ioread16(dpld->dp_base);
+
+ iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->txq.tail)[0]),
+ dpld->address_buffer);
+ val2 = ioread16(dpld->dp_base);
+
+ if (likely(val1 == val2)) {
+ spin_unlock_irqrestore(&dpld->pld_lock, flags);
+ return val1;
+ }
+
+ mif_err("ERR: %s txq.tail(%d) != in(%d)\n",
+ get_dev_name(id), val1, val2);
+ udelay(100);
+
+ } while (cnt--);
+
+ spin_unlock_irqrestore(&dpld->pld_lock, flags);
+ return val1;
+}
+
+static inline void set_tx_head(struct dpram_link_device *dpld, int id, u32 in)
+{
+ int cnt = 3;
+ u32 val = 0;
+ unsigned long int flags;
+
+ spin_lock_irqsave(&dpld->pld_lock, flags);
+
+ iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->txq.head)[0]),
+ dpld->address_buffer);
+ iowrite16((u16)in, dpld->dp_base);
+
+ do {
+ /* Check head value written */
+ iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->txq.head)[0]),
+ dpld->address_buffer);
+ val = ioread16(dpld->dp_base);
+
+ if (likely(val == in)) {
+ spin_unlock_irqrestore(&dpld->pld_lock, flags);
+ return;
+ }
+
+ mif_err("ERR: %s txq.head(%d) != in(%d)\n",
+ get_dev_name(id), val, in);
+ udelay(100);
+
+ /* Write head value again */
+ iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->txq.head)[0]),
+ dpld->address_buffer);
+ iowrite16((u16)in, dpld->dp_base);
+ } while (cnt--);
+
+ spin_unlock_irqrestore(&dpld->pld_lock, flags);
+ return;
+}
+
+static inline void set_tx_tail(struct dpram_link_device *dpld, int id, u32 out)
+{
+ return;
+}
+
+static inline u8 *get_tx_buff(struct dpram_link_device *dpld, int id)
+{
+ return dpld->dev[id]->txq.buff;
+}
+
+static inline u32 get_tx_buff_size(struct dpram_link_device *dpld, int id)
+{
+ return dpld->dev[id]->txq.size;
+}
+
+static inline u32 get_rx_head(struct dpram_link_device *dpld, int id)
+{
+ u16 val1 = 0, val2 = 0, cnt = 3;
+ unsigned long int flags;
+
+ spin_lock_irqsave(&dpld->pld_lock, flags);
+
+ do {
+ /* Check head value written */
+ iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->rxq.head)[0]),
+ dpld->address_buffer);
+ val1 = ioread16(dpld->dp_base);
+
+ iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->rxq.head)[0]),
+ dpld->address_buffer);
+ val2 = ioread16(dpld->dp_base);
+
+ if (likely(val1 == val2)) {
+ spin_unlock_irqrestore(&dpld->pld_lock, flags);
+ return val1;
+ }
+
+ mif_err("ERR: %s rxq.head(%d) != in(%d)\n",
+ get_dev_name(id), val1, val2);
+ udelay(100);
+
+ } while (cnt--);
+
+ spin_unlock_irqrestore(&dpld->pld_lock, flags);
+ return val1;
+}
+
+static inline u32 get_rx_tail(struct dpram_link_device *dpld, int id)
+{
+ u16 val1 = 0, val2 = 0, cnt = 3;
+ unsigned long int flags;
+
+ spin_lock_irqsave(&dpld->pld_lock, flags);
+
+ do {
+ /* Check head value written */
+ iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->rxq.tail)[0]),
+ dpld->address_buffer);
+ val1 = ioread16(dpld->dp_base);
+
+ iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->rxq.tail)[0]),
+ dpld->address_buffer);
+ val2 = ioread16(dpld->dp_base);
+
+ if (likely(val1 == val2)) {
+ spin_unlock_irqrestore(&dpld->pld_lock, flags);
+ return val1;
+ }
+
+ mif_err("ERR: %s rxq.tail(%d) != in(%d)\n",
+ get_dev_name(id), val1, val2);
+ udelay(100);
+
+ } while (cnt--);
+
+ spin_unlock_irqrestore(&dpld->pld_lock, flags);
+ return val1;
+}
+
+static inline void set_rx_head(struct dpram_link_device *dpld, int id, u32 in)
+{
+ return;
+}
+
+static inline void set_rx_tail(struct dpram_link_device *dpld, int id, u32 out)
+{
+ int cnt = 3;
+ u32 val = 0;
+ unsigned long int flags;
+
+ spin_lock_irqsave(&dpld->pld_lock, flags);
+
+ iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->rxq.tail)[0]),
+ dpld->address_buffer);
+ iowrite16((u16)out, dpld->dp_base);
+
+ do {
+ /* Check tail value written */
+ iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->rxq.tail)[0]),
+ dpld->address_buffer);
+ val = ioread16(dpld->dp_base);
+
+ if (val == out) {
+ spin_unlock_irqrestore(&dpld->pld_lock, flags);
+ return;
+ }
+
+ mif_err("ERR: %s rxq.tail(%d) != out(%d)\n",
+ get_dev_name(id), val, out);
+ udelay(100);
+
+ /* Write tail value again */
+ iowrite16(PLD_ADDR_MASK(&(dpld->dev[id]->rxq.tail)[0]),
+ dpld->address_buffer);
+ iowrite16((u16)out, dpld->dp_base);
+ } while (cnt--);
+
+ spin_unlock_irqrestore(&dpld->pld_lock, flags);
+ return;
+}
+
+static inline u8 *get_rx_buff(struct dpram_link_device *dpld, int id)
+{
+ return dpld->dev[id]->rxq.buff;
+}
+
+static inline u32 get_rx_buff_size(struct dpram_link_device *dpld, int id)
+{
+ return dpld->dev[id]->rxq.size;
+}
+
+static inline u16 get_mask_req_ack(struct dpram_link_device *dpld, int id)
+{
+ return dpld->dev[id]->mask_req_ack;
+}
+
+static inline u16 get_mask_res_ack(struct dpram_link_device *dpld, int id)
+{
+ return dpld->dev[id]->mask_res_ack;
+}
+
+static inline u16 get_mask_send(struct dpram_link_device *dpld, int id)
+{
+ return dpld->dev[id]->mask_send;
+}
+
+static inline bool dpram_circ_valid(u32 size, u32 in, u32 out)
+{
+ if (in >= size)
+ return false;
+
+ if (out >= size)
+ return false;
+
+ return true;
+}
+
+/* Get free space in the TXQ as well as in & out pointers */
+static inline int dpram_get_txq_space(struct dpram_link_device *dpld, int dev,
+ u32 qsize, u32 *in, u32 *out)
+{
+ struct link_device *ld = &dpld->ld;
+ int cnt = 3;
+ u32 head;
+ u32 tail;
+ int space;
+
+ do {
+ head = get_tx_head(dpld, dev);
+ tail = get_tx_tail(dpld, dev);
+
+ space = (head < tail) ? (tail - head - 1) :
+ (qsize + tail - head - 1);
+ mif_debug("%s: %s_TXQ qsize[%u] in[%u] out[%u] space[%u]\n",
+ ld->name, get_dev_name(dev), qsize, head, tail, space);
+
+ if (dpram_circ_valid(qsize, head, tail)) {
+ *in = head;
+ *out = tail;
+ return space;
+ }
+
+ mif_info("%s: CAUTION! <%pf> "
+ "%s_TXQ invalid (size:%d in:%d out:%d)\n",
+ ld->name, __builtin_return_address(0),
+ get_dev_name(dev), qsize, head, tail);
+
+ udelay(100);
+ } while (cnt--);
+
+ *in = 0;
+ *out = 0;
+ return -EINVAL;
+}
+
+static void dpram_reset_tx_circ(struct dpram_link_device *dpld, int dev)
+{
+ set_tx_head(dpld, dev, 0);
+ set_tx_tail(dpld, dev, 0);
+ if (dev == IPC_FMT)
+ trigger_force_cp_crash(dpld);
+}
+
+/* Get data size in the RXQ as well as in & out pointers */
+static inline int dpram_get_rxq_rcvd(struct dpram_link_device *dpld, int dev,
+ u32 qsize, u32 *in, u32 *out)
+{
+ struct link_device *ld = &dpld->ld;
+ int cnt = 3;
+ u32 head;
+ u32 tail;
+ u32 rcvd;
+
+ do {
+ head = get_rx_head(dpld, dev);
+ tail = get_rx_tail(dpld, dev);
+ if (head == tail) {
+ *in = head;
+ *out = tail;
+ return 0;
+ }
+
+ rcvd = (head > tail) ? (head - tail) : (qsize - tail + head);
+ mif_info("%s: %s_RXQ qsize[%u] in[%u] out[%u] rcvd[%u]\n",
+ ld->name, get_dev_name(dev), qsize, head, tail, rcvd);
+
+ if (dpram_circ_valid(qsize, head, tail)) {
+ *in = head;
+ *out = tail;
+ return rcvd;
+ }
+
+ mif_info("%s: CAUTION! <%pf> "
+ "%s_RXQ invalid (size:%d in:%d out:%d)\n",
+ ld->name, __builtin_return_address(0),
+ get_dev_name(dev), qsize, head, tail);
+
+ udelay(100);
+ } while (cnt--);
+
+ *in = 0;
+ *out = 0;
+ return -EINVAL;
+}
+
+static void dpram_reset_rx_circ(struct dpram_link_device *dpld, int dev)
+{
+ set_rx_head(dpld, dev, 0);
+ set_rx_tail(dpld, dev, 0);
+ if (dev == IPC_FMT)
+ trigger_force_cp_crash(dpld);
+}
+
+/*
+** CAUTION : dpram_allow_sleep() MUST be invoked after dpram_wake_up() success
+*/
+static int dpram_wake_up(struct dpram_link_device *dpld)
+{
+ struct link_device *ld = &dpld->ld;
+
+ if (!dpld->dpctl->wakeup)
+ return 0;
+
+ if (dpld->dpctl->wakeup() < 0) {
+ mif_err("%s: ERR! <%pf> DPRAM wakeup fail\n",
+ ld->name, __builtin_return_address(0));
+ return -EACCES;
+ }
+
+ atomic_inc(&dpld->accessing);
+ return 0;
+}
+
+static void dpram_allow_sleep(struct dpram_link_device *dpld)
+{
+ struct link_device *ld = &dpld->ld;
+
+ if (!dpld->dpctl->sleep)
+ return;
+
+ if (atomic_dec_return(&dpld->accessing) <= 0) {
+ dpld->dpctl->sleep();
+ atomic_set(&dpld->accessing, 0);
+ mif_debug("%s: DPRAM sleep possible\n", ld->name);
+ }
+}
+
+static int dpram_check_access(struct dpram_link_device *dpld)
+{
+ struct link_device *ld = &dpld->ld;
+ int i;
+ u16 magic = get_magic(dpld);
+ u16 access = get_access(dpld);
+
+ if (likely(magic == DPRAM_MAGIC_CODE && access == 1))
+ return 0;
+
+ for (i = 1; i <= 100; i++) {
+ mif_info("%s: ERR! magic:%X access:%X -> retry:%d\n",
+ ld->name, magic, access, i);
+ udelay(100);
+
+ magic = get_magic(dpld);
+ access = get_access(dpld);
+ if (likely(magic == DPRAM_MAGIC_CODE && access == 1))
+ return 0;
+ }
+
+ mif_info("%s: !CRISIS! magic:%X access:%X\n", ld->name, magic, access);
+ return -EACCES;
+}
+
+static bool dpram_ipc_active(struct dpram_link_device *dpld)
+{
+ struct link_device *ld = &dpld->ld;
+
+ /* Check DPRAM mode */
+ if (ld->mode != LINK_MODE_IPC) {
+ mif_info("%s: <%pf> ld->mode != LINK_MODE_IPC\n",
+ ld->name, __builtin_return_address(0));
+ return false;
+ }
+
+ if (dpram_check_access(dpld) < 0) {
+ mif_info("%s: ERR! <%pf> dpram_check_access fail\n",
+ ld->name, __builtin_return_address(0));
+ return false;
+ }
+
+ return true;
+}
+
+static void dpram_ipc_write(struct dpram_link_device *dpld, int dev,
+ u32 qsize, u32 in, u32 out, struct sk_buff *skb)
+{
+ struct link_device *ld = &dpld->ld;
+ u8 __iomem *buff = get_tx_buff(dpld, dev);
+ u8 *src = skb->data;
+ u32 len = skb->len;
+ u32 inp;
+ struct mif_irq_map map;
+
+ if (in < out) {
+ /* +++++++++ in ---------- out ++++++++++ */
+ iowrite16(PLD_ADDR_MASK(&(buff+in)[0]), dpld->address_buffer);
+ memcpy(dpld->dp_base, src, len);
+ } else {
+ /* ------ out +++++++++++ in ------------ */
+ u32 space = qsize - in;
+
+ /* 1) in -> buffer end */
+ iowrite16(PLD_ADDR_MASK(&(buff+in)[0]), dpld->address_buffer);
+ memcpy(dpld->dp_base, src, ((len > space) ? space : len));
+
+ if (len > space) {
+ iowrite16(PLD_ADDR_MASK(&buff[0]),
+ dpld->address_buffer);
+ memcpy(dpld->dp_base, (src+space), (len-space));
+ }
+ }
+
+ /* update new in pointer */
+ inp = in + len;
+ if (inp >= qsize)
+ inp -= qsize;
+ set_tx_head(dpld, dev, inp);
+
+ if (dev == IPC_FMT) {
+ set_dpram_map(dpld, &map);
+ mif_irq_log(ld->mc->msd, map, "ipc_write", sizeof("ipc_write"));
+ mif_ipc_log(MIF_IPC_AP2CP, ld->mc->msd, skb->data, skb->len);
+ }
+}
+
+static int dpram_try_ipc_tx(struct dpram_link_device *dpld, int dev)
+{
+ struct link_device *ld = &dpld->ld;
+ struct sk_buff_head *txq = ld->skb_txq[dev];
+ struct sk_buff *skb;
+ unsigned long int flags;
+ u32 qsize = get_tx_buff_size(dpld, dev);
+ u32 in;
+ u32 out;
+ int space;
+ int copied = 0;
+
+ spin_lock_irqsave(&dpld->tx_rx_lock, flags);
+
+ while (1) {
+ space = dpram_get_txq_space(dpld, dev, qsize, &in, &out);
+ if (unlikely(space < 0)) {
+ spin_unlock_irqrestore(&dpld->tx_rx_lock, flags);
+ dpram_reset_tx_circ(dpld, dev);
+ return space;
+ }
+
+ skb = skb_dequeue(txq);
+ if (unlikely(!skb))
+ break;
+
+ if (unlikely(space < skb->len)) {
+ atomic_set(&dpld->res_required[dev], 1);
+ skb_queue_head(txq, skb);
+ spin_unlock_irqrestore(&dpld->tx_rx_lock, flags);
+ mif_info("%s: %s "
+ "qsize[%u] in[%u] out[%u] free[%u] < len[%u]\n",
+ ld->name, get_dev_name(dev),
+ qsize, in, out, space, skb->len);
+ return -ENOSPC;
+ }
+
+ /* TX if there is enough room in the queue */
+ dpram_ipc_write(dpld, dev, qsize, in, out, skb);
+ copied += skb->len;
+ dev_kfree_skb_any(skb);
+ }
+
+ spin_unlock_irqrestore(&dpld->tx_rx_lock, flags);
+
+ return copied;
+}
+
+static void dpram_ipc_rx_task(unsigned long data)
+{
+ struct dpram_link_device *dpld = (struct dpram_link_device *)data;
+ struct link_device *ld = &dpld->ld;
+ struct io_device *iod;
+ struct dpram_rxb *rxb;
+ unsigned qlen;
+ int i;
+
+ for (i = 0; i < dpld->max_ipc_dev; i++) {
+ iod = dpld->iod[i];
+ qlen = rxbq_size(&dpld->rxbq[i]);
+ while (qlen > 0) {
+ rxb = rxbq_get_data_rxb(&dpld->rxbq[i]);
+ iod->recv(iod, ld, rxb->data, rxb->len);
+ rxb_clear(rxb);
+ qlen--;
+ }
+ }
+}
+
+static void dpram_ipc_read(struct dpram_link_device *dpld, int dev, u8 *dst,
+ u8 __iomem *src, u32 out, u32 len, u32 qsize)
+{
+ u8 *ori_det = dst;
+ unsigned long flags;
+
+ if ((out + len) <= qsize) {
+ /* ----- (out) (in) ----- */
+ /* ----- 7f 00 00 7e ----- */
+ iowrite16(PLD_ADDR_MASK(&(src+out)[0]), dpld->address_buffer);
+ memcpy(dst, dpld->dp_base, len);
+ } else {
+ /* (in) ----------- (out) */
+ /* 00 7e ----------- 7f 00 */
+ unsigned len1 = qsize - out;
+
+ /* 1) out -> buffer end */
+ iowrite16(PLD_ADDR_MASK(&(src+out)[0]), dpld->address_buffer);
+ memcpy(dst, dpld->dp_base, len1);
+
+ /* 2) buffer start -> in */
+ dst += len1;
+ iowrite16(PLD_ADDR_MASK(&src[0]), dpld->address_buffer);
+ memcpy(dst, dpld->dp_base, (len - len1));
+ }
+
+ if (dpld->ld.mode == LINK_MODE_IPC && ori_det[0] != 0x7F) {
+ mif_info("ipc read error!! in[%d], out[%d]\n",
+ get_rx_head(dpld, dev),
+ get_rx_tail(dpld, dev));
+ }
+
+}
+
+/*
+ ret < 0 : error
+ ret == 0 : no data
+ ret > 0 : valid data
+*/
+static int dpram_ipc_recv_data_with_rxb(struct dpram_link_device *dpld, int dev)
+{
+ struct link_device *ld = &dpld->ld;
+ struct dpram_rxb *rxb;
+ u8 __iomem *src = get_rx_buff(dpld, dev);
+ u32 qsize = get_rx_buff_size(dpld, dev);
+ u32 in;
+ u32 out;
+ u32 rcvd;
+ struct mif_irq_map map;
+ unsigned long int flags;
+
+ spin_lock_irqsave(&dpld->tx_rx_lock, flags);
+
+ rcvd = dpram_get_rxq_rcvd(dpld, dev, qsize, &in, &out);
+ if (rcvd <= 0) {
+ spin_unlock_irqrestore(&dpld->tx_rx_lock, flags);
+ return rcvd;
+ }
+
+ if (dev == IPC_FMT) {
+ set_dpram_map(dpld, &map);
+ mif_irq_log(ld->mc->msd, map, "ipc_recv", sizeof("ipc_recv"));
+ }
+
+ /* Allocate an rxb */
+ rxb = rxbq_get_free_rxb(&dpld->rxbq[dev]);
+ if (!rxb) {
+ mif_info("%s: ERR! %s rxbq_get_free_rxb fail\n",
+ ld->name, get_dev_name(dev));
+ spin_unlock_irqrestore(&dpld->tx_rx_lock, flags);
+ return -ENOMEM;
+ }
+
+ /* Read data from each DPRAM buffer */
+ dpram_ipc_read(dpld, dev, rxb_put(rxb, rcvd), src, out, rcvd, qsize);
+
+ /* Calculate and set new out */
+ out += rcvd;
+ if (out >= qsize)
+ out -= qsize;
+ set_rx_tail(dpld, dev, out);
+
+ spin_unlock_irqrestore(&dpld->tx_rx_lock, flags);
+ return rcvd;
+}
+
+/*
+ ret < 0 : error
+ ret == 0 : no data
+ ret > 0 : valid data
+*/
+static int dpram_ipc_recv_data_with_skb(struct dpram_link_device *dpld, int dev)
+{
+ struct link_device *ld = &dpld->ld;
+ struct io_device *iod = dpld->iod[dev];
+ struct sk_buff *skb;
+ u8 __iomem *src = get_rx_buff(dpld, dev);
+ u32 qsize = get_rx_buff_size(dpld, dev);
+ u32 in;
+ u32 out;
+ u32 rcvd;
+ int rest;
+ u8 *frm;
+ u8 *dst;
+ unsigned int len;
+ unsigned int pad;
+ unsigned int tot;
+ struct mif_irq_map map;
+
+ rcvd = dpram_get_rxq_rcvd(dpld, dev, qsize, &in, &out);
+ if (rcvd <= 0)
+ return rcvd;
+
+ if (dev == IPC_FMT) {
+ set_dpram_map(dpld, &map);
+ mif_irq_log(ld->mc->msd, map, "ipc_recv", sizeof("ipc_recv"));
+ }
+
+ rest = rcvd;
+ while (rest > 0) {
+ frm = src + out;
+ if (unlikely(!sipc5_start_valid(frm[0]))) {
+ mif_err("%s: ERR! %s invalid start 0x%02X\n",
+ ld->name, get_dev_name(dev), frm[0]);
+ skb_queue_purge(&dpld->skb_rxq[dev]);
+ return -EBADMSG;
+ }
+
+ len = sipc5_get_frame_sz16(frm);
+ if (unlikely(len > rest)) {
+ mif_err("%s: ERR! %s len %d > rest %d\n",
+ ld->name, get_dev_name(dev), len, rest);
+ skb_queue_purge(&dpld->skb_rxq[dev]);
+ return -EBADMSG;
+ }
+
+ pad = sipc5_calc_padding_size(len);
+ tot = len + pad;
+
+ /* Allocate an skb */
+ skb = dev_alloc_skb(tot);
+ if (!skb) {
+ mif_err("%s: ERR! %s dev_alloc_skb fail\n",
+ ld->name, get_dev_name(dev));
+ return -ENOMEM;
+ }
+
+ /* Read data from each DPRAM buffer */
+ dst = skb_put(skb, tot);
+ dpram_ipc_read(dpld, dev, dst, src, out, tot, qsize);
+ skb_trim(skb, len);
+ iod->recv_skb(iod, ld, skb);
+
+ /* Calculate and set new out */
+ rest -= tot;
+ out += tot;
+ if (out >= qsize)
+ out -= qsize;
+ }
+
+ set_rx_tail(dpld, dev, out);
+
+ return rcvd;
+}
+
+static void non_command_handler(struct dpram_link_device *dpld, u16 non_cmd)
+{
+ struct link_device *ld = &dpld->ld;
+ int i = 0;
+ int ret = 0;
+ u16 tx_mask = 0;
+
+ if (!dpram_ipc_active(dpld))
+ return;
+
+ /* Read data from DPRAM */
+ for (i = 0; i < dpld->max_ipc_dev; i++) {
+ if (dpld->use_skb)
+ ret = dpram_ipc_recv_data_with_skb(dpld, i);
+ else
+ ret = dpram_ipc_recv_data_with_rxb(dpld, i);
+ if (ret < 0)
+ dpram_reset_rx_circ(dpld, i);
+
+ /* Check and process REQ_ACK (at this time, in == out) */
+ if (non_cmd & get_mask_req_ack(dpld, i)) {
+ mif_debug("%s: send %s_RES_ACK\n",
+ ld->name, get_dev_name(i));
+ tx_mask |= get_mask_res_ack(dpld, i);
+ }
+ }
+
+ if (!dpld->use_skb) {
+ /* Schedule soft IRQ for RX */
+ tasklet_hi_schedule(&dpld->rx_tsk);
+ }
+
+ /* Try TX via DPRAM */
+ for (i = 0; i < dpld->max_ipc_dev; i++) {
+ if (atomic_read(&dpld->res_required[i]) > 0) {
+ ret = dpram_try_ipc_tx(dpld, i);
+ if (ret > 0) {
+ atomic_set(&dpld->res_required[i], 0);
+ tx_mask |= get_mask_send(dpld, i);
+ } else if (ret == -ENOSPC) {
+ tx_mask |= get_mask_req_ack(dpld, i);
+ }
+ }
+ }
+
+ if (tx_mask) {
+ send_intr(dpld, INT_NON_CMD(tx_mask));
+ mif_debug("%s: send intr 0x%04X\n", ld->name, tx_mask);
+ }
+}
+
+static void handle_cp_crash(struct dpram_link_device *dpld)
+{
+ struct link_device *ld = &dpld->ld;
+ struct io_device *iod;
+ int i;
+
+ for (i = 0; i < dpld->max_ipc_dev; i++) {
+ mif_info("%s: purging %s_skb_txq\b", ld->name, get_dev_name(i));
+ skb_queue_purge(ld->skb_txq[i]);
+ }
+
+ iod = link_get_iod_with_format(ld, IPC_FMT);
+ iod->modem_state_changed(iod, STATE_CRASH_EXIT);
+
+ iod = link_get_iod_with_format(ld, IPC_BOOT);
+ iod->modem_state_changed(iod, STATE_CRASH_EXIT);
+
+ iod = link_get_iod_with_channel(ld, PS_DATA_CH_0);
+ if (iod)
+ iodevs_for_each(iod->msd, iodev_netif_stop, 0);
+}
+
+static void handle_no_crash_ack(unsigned long arg)
+{
+ struct dpram_link_device *dpld = (struct dpram_link_device *)arg;
+ struct link_device *ld = &dpld->ld;
+
+ mif_err("%s: ERR! No CRASH_EXIT ACK from CP\n", ld->mc->name);
+
+ if (!wake_lock_active(&dpld->wlock))
+ wake_lock(&dpld->wlock);
+
+ handle_cp_crash(dpld);
+}
+
+static int trigger_force_cp_crash(struct dpram_link_device *dpld)
+{
+ struct link_device *ld = &dpld->ld;
+
+ if (ld->mode == LINK_MODE_ULOAD) {
+ mif_err("%s: CP crash is already in progress\n", ld->mc->name);
+ return 0;
+ }
+
+ ld->mode = LINK_MODE_ULOAD;
+ mif_err("%s: called by %pf\n", ld->name, __builtin_return_address(0));
+
+ dpram_wake_up(dpld);
+
+ send_intr(dpld, INT_CMD(INT_CMD_CRASH_EXIT));
+
+ mif_add_timer(&dpld->crash_ack_timer, FORCE_CRASH_ACK_TIMEOUT,
+ handle_no_crash_ack, (unsigned long)dpld);
+
+ return 0;
+}
+
+static int dpram_init_ipc(struct dpram_link_device *dpld)
+{
+ struct link_device *ld = &dpld->ld;
+ int i;
+
+ if (ld->mode == LINK_MODE_IPC &&
+ get_magic(dpld) == DPRAM_MAGIC_CODE &&
+ get_access(dpld) == 1)
+ mif_info("%s: IPC already initialized\n", ld->name);
+
+ /* Clear pointers in every circular queue */
+ for (i = 0; i < dpld->max_ipc_dev; i++) {
+ set_tx_head(dpld, i, 0);
+ set_tx_tail(dpld, i, 0);
+ set_rx_head(dpld, i, 0);
+ set_rx_tail(dpld, i, 0);
+ }
+
+ /* Initialize variables for efficient TX/RX processing */
+ for (i = 0; i < dpld->max_ipc_dev; i++)
+ dpld->iod[i] = link_get_iod_with_format(ld, i);
+ dpld->iod[IPC_RAW] = link_get_iod_with_format(ld, IPC_MULTI_RAW);
+
+ if (dpld->iod[IPC_RAW]->recv_skb)
+ dpld->use_skb = true;
+
+ for (i = 0; i < dpld->max_ipc_dev; i++) {
+ atomic_set(&dpld->res_required[i], 0);
+ skb_queue_purge(&dpld->skb_rxq[i]);
+ }
+
+ spin_lock_init(&dpld->tx_rx_lock);
+
+ /* Enable IPC */
+ atomic_set(&dpld->accessing, 0);
+
+ set_magic(dpld, DPRAM_MAGIC_CODE);
+ set_access(dpld, 1);
+ if (get_magic(dpld) != DPRAM_MAGIC_CODE || get_access(dpld) != 1)
+ return -EACCES;
+
+ ld->mode = LINK_MODE_IPC;
+
+ if (wake_lock_active(&dpld->wlock))
+ wake_unlock(&dpld->wlock);
+
+ return 0;
+}
+
+static void cmd_req_active_handler(struct dpram_link_device *dpld)
+{
+ send_intr(dpld, INT_CMD(INT_CMD_RES_ACTIVE));
+}
+
+static void cmd_crash_reset_handler(struct dpram_link_device *dpld)
+{
+ struct link_device *ld = &dpld->ld;
+ struct io_device *iod = NULL;
+
+ ld->mode = LINK_MODE_ULOAD;
+
+ if (!wake_lock_active(&dpld->wlock))
+ wake_lock(&dpld->wlock);
+
+ mif_err("%s: Recv 0xC7 (CRASH_RESET)\n", ld->name);
+
+ iod = link_get_iod_with_format(ld, IPC_FMT);
+ iod->modem_state_changed(iod, STATE_CRASH_RESET);
+
+ iod = link_get_iod_with_format(ld, IPC_BOOT);
+ iod->modem_state_changed(iod, STATE_CRASH_RESET);
+}
+
+static void cmd_crash_exit_handler(struct dpram_link_device *dpld)
+{
+ struct link_device *ld = &dpld->ld;
+
+ ld->mode = LINK_MODE_ULOAD;
+
+ if (!wake_lock_active(&dpld->wlock))
+ wake_lock(&dpld->wlock);
+
+ mif_err("%s: Recv 0xC9 (CRASH_EXIT)\n", ld->name);
+
+ dpram_wake_up(dpld);
+
+ del_timer(&dpld->crash_ack_timer);
+
+ if (dpld->ext_op && dpld->ext_op->crash_log)
+ dpld->ext_op->crash_log(dpld);
+
+ handle_cp_crash(dpld);
+}
+
+static void cmd_phone_start_handler(struct dpram_link_device *dpld)
+{
+ struct link_device *ld = &dpld->ld;
+ struct io_device *iod = NULL;
+
+ mif_info("%s: Recv 0xC8 (CP_START)\n", ld->name);
+
+ dpram_init_ipc(dpld);
+
+ iod = link_get_iod_with_format(ld, IPC_FMT);
+ if (!iod) {
+ mif_info("%s: ERR! no iod\n", ld->name);
+ return;
+ }
+
+ if (dpld->ext_op && dpld->ext_op->cp_start_handler)
+ dpld->ext_op->cp_start_handler(dpld);
+
+ if (ld->mc->phone_state != STATE_ONLINE) {
+ mif_info("%s: phone_state: %d -> ONLINE\n",
+ ld->name, ld->mc->phone_state);
+ iod->modem_state_changed(iod, STATE_ONLINE);
+ }
+
+ mif_info("%s: Send 0xC2 (INIT_END)\n", ld->name);
+ send_intr(dpld, INT_CMD(INT_CMD_INIT_END));
+}
+
+static void command_handler(struct dpram_link_device *dpld, u16 cmd)
+{
+ struct link_device *ld = &dpld->ld;
+
+ switch (INT_CMD_MASK(cmd)) {
+ case INT_CMD_REQ_ACTIVE:
+ cmd_req_active_handler(dpld);
+ break;
+
+ case INT_CMD_CRASH_RESET:
+ dpld->dpram_init_status = DPRAM_INIT_STATE_NONE;
+ cmd_crash_reset_handler(dpld);
+ break;
+
+ case INT_CMD_CRASH_EXIT:
+ dpld->dpram_init_status = DPRAM_INIT_STATE_NONE;
+ cmd_crash_exit_handler(dpld);
+ break;
+
+ case INT_CMD_PHONE_START:
+ dpld->dpram_init_status = DPRAM_INIT_STATE_READY;
+ cmd_phone_start_handler(dpld);
+ complete_all(&dpld->dpram_init_cmd);
+ break;
+
+ case INT_CMD_NV_REBUILDING:
+ mif_info("%s: NV_REBUILDING\n", ld->name);
+ break;
+
+ case INT_CMD_PIF_INIT_DONE:
+ complete_all(&dpld->modem_pif_init_done);
+ break;
+
+ case INT_CMD_SILENT_NV_REBUILDING:
+ mif_info("%s: SILENT_NV_REBUILDING\n", ld->name);
+ break;
+
+ case INT_CMD_NORMAL_PWR_OFF:
+ /*ToDo:*/
+ /*kernel_sec_set_cp_ack()*/;
+ break;
+
+ case INT_CMD_REQ_TIME_SYNC:
+ case INT_CMD_CP_DEEP_SLEEP:
+ case INT_CMD_EMER_DOWN:
+ break;
+
+ default:
+ mif_info("%s: unknown command 0x%04X\n", ld->name, cmd);
+ }
+}
+
+static void ext_command_handler(struct dpram_link_device *dpld, u16 cmd)
+{
+ struct link_device *ld = &dpld->ld;
+ u16 resp;
+
+ switch (EXT_CMD_MASK(cmd)) {
+ case EXT_CMD_SET_SPEED_LOW:
+ if (dpld->dpctl->setup_speed) {
+ dpld->dpctl->setup_speed(DPRAM_SPEED_LOW);
+ resp = INT_EXT_CMD(EXT_CMD_SET_SPEED_LOW);
+ send_intr(dpld, resp);
+ }
+ break;
+
+ case EXT_CMD_SET_SPEED_MID:
+ if (dpld->dpctl->setup_speed) {
+ dpld->dpctl->setup_speed(DPRAM_SPEED_MID);
+ resp = INT_EXT_CMD(EXT_CMD_SET_SPEED_MID);
+ send_intr(dpld, resp);
+ }
+ break;
+
+ case EXT_CMD_SET_SPEED_HIGH:
+ if (dpld->dpctl->setup_speed) {
+ dpld->dpctl->setup_speed(DPRAM_SPEED_HIGH);
+ resp = INT_EXT_CMD(EXT_CMD_SET_SPEED_HIGH);
+ send_intr(dpld, resp);
+ }
+ break;
+
+ default:
+ mif_info("%s: unknown command 0x%04X\n", ld->name, cmd);
+ break;
+ }
+}
+
+static void udl_command_handler(struct dpram_link_device *dpld, u16 cmd)
+{
+ struct link_device *ld = &dpld->ld;
+
+ if (cmd & UDL_RESULT_FAIL) {
+ mif_info("%s: ERR! Command failed: %04x\n", ld->name, cmd);
+ return;
+ }
+
+ switch (UDL_CMD_MASK(cmd)) {
+ case UDL_CMD_RECV_READY:
+ mif_debug("%s: Send CP-->AP RECEIVE_READY\n", ld->name);
+ send_intr(dpld, CMD_IMG_START_REQ);
+ break;
+ default:
+ complete_all(&dpld->udl_cmd_complete);
+ }
+}
+
+static irqreturn_t dpram_irq_handler(int irq, void *data)
+{
+ struct dpram_link_device *dpld = (struct dpram_link_device *)data;
+ struct link_device *ld = (struct link_device *)&dpld->ld;
+ u16 int2ap = 0;
+
+ if (unlikely(ld->mode == LINK_MODE_OFFLINE))
+ return IRQ_HANDLED;
+
+ if (dpram_wake_up(dpld) < 0) {
+ log_dpram_status(dpld);
+ trigger_force_cp_crash(dpld);
+ return IRQ_HANDLED;
+ }
+
+ int2ap = recv_intr(dpld);
+
+ if (unlikely(int2ap == INT_POWERSAFE_FAIL)) {
+ mif_info("%s: int2ap == INT_POWERSAFE_FAIL\n", ld->name);
+ goto exit;
+ } else if (int2ap == 0x1234 || int2ap == 0xDBAB || int2ap == 0xABCD) {
+ if (dpld->ext_op && dpld->ext_op->dload_cmd_handler) {
+ dpld->ext_op->dload_cmd_handler(dpld, int2ap);
+ goto exit;
+ }
+ }
+
+ if (unlikely(EXT_UDL_CMD(int2ap))) {
+ if (likely(EXT_INT_VALID(int2ap))) {
+ if (UDL_CMD_VALID(int2ap))
+ udl_command_handler(dpld, int2ap);
+ else if (EXT_CMD_VALID(int2ap))
+ ext_command_handler(dpld, int2ap);
+ else
+ mif_info("%s: ERR! invalid intr 0x%04X\n",
+ ld->name, int2ap);
+ } else {
+ mif_info("%s: ERR! invalid intr 0x%04X\n",
+ ld->name, int2ap);
+ }
+ } else {
+ if (likely(INT_VALID(int2ap))) {
+ if (unlikely(INT_CMD_VALID(int2ap)))
+ command_handler(dpld, int2ap);
+ else
+ non_command_handler(dpld, int2ap);
+ } else {
+ mif_info("%s: ERR! invalid intr 0x%04X\n",
+ ld->name, int2ap);
+ }
+ }
+
+exit:
+ clear_intr(dpld);
+ dpram_allow_sleep(dpld);
+ return IRQ_HANDLED;
+}
+
+static void dpram_send_ipc(struct link_device *ld, int dev,
+ struct io_device *iod, struct sk_buff *skb)
+{
+ struct dpram_link_device *dpld = to_dpram_link_device(ld);
+ struct sk_buff_head *txq = ld->skb_txq[dev];
+ int ret;
+ u16 mask;
+
+ skb_queue_tail(txq, skb);
+ if (txq->qlen > 1024) {
+ mif_debug("%s: %s txq->qlen %d > 1024\n",
+ ld->name, get_dev_name(dev), txq->qlen);
+ }
+
+ if (dpram_wake_up(dpld) < 0) {
+ trigger_force_cp_crash(dpld);
+ return;
+ }
+
+ if (!dpram_ipc_active(dpld))
+ goto exit;
+
+ if (atomic_read(&dpld->res_required[dev]) > 0) {
+ mif_debug("%s: %s_TXQ is full\n", ld->name, get_dev_name(dev));
+ goto exit;
+ }
+
+ ret = dpram_try_ipc_tx(dpld, dev);
+ if (ret > 0) {
+ mask = get_mask_send(dpld, dev);
+ send_intr(dpld, INT_NON_CMD(mask));
+ } else if (ret == -ENOSPC) {
+ mask = get_mask_req_ack(dpld, dev);
+ send_intr(dpld, INT_NON_CMD(mask));
+ mif_info("%s: Send REQ_ACK 0x%04X\n", ld->name, mask);
+ } else {
+ mif_info("%s: dpram_try_ipc_tx fail (err %d)\n", ld->name, ret);
+ }
+
+exit:
+ dpram_allow_sleep(dpld);
+}
+
+static int dpram_download_bin(struct link_device *ld, struct sk_buff *skb)
+{
+ struct dpram_link_device *dpld = to_dpram_link_device(ld);
+
+ if (dpld->ext_op && dpld->ext_op->dload_bin)
+ return dpld->ext_op->dload_bin(dpld, skb);
+ else
+ return -ENODEV;
+}
+
+static int dpram_send(struct link_device *ld, struct io_device *iod,
+ struct sk_buff *skb)
+{
+ enum dev_format dev = iod->format;
+ int len = skb->len;
+
+ switch (dev) {
+ case IPC_FMT:
+ case IPC_RAW:
+ case IPC_RFS:
+ if (likely(ld->mode == LINK_MODE_IPC)) {
+ dpram_send_ipc(ld, dev, iod, skb);
+ } else {
+ mif_info("%s: ld->mode != LINK_MODE_IPC\n", ld->name);
+ dev_kfree_skb_any(skb);
+ }
+ return len;
+
+ case IPC_BOOT:
+ return dpram_download_bin(ld, skb);
+
+ default:
+ mif_info("%s: ERR! no TXQ for %s\n", ld->name, iod->name);
+ dev_kfree_skb_any(skb);
+ return -ENODEV;
+ }
+}
+
+static int dpram_force_dump(struct link_device *ld, struct io_device *iod)
+{
+ struct dpram_link_device *dpld = to_dpram_link_device(ld);
+ trigger_force_cp_crash(dpld);
+ return 0;
+}
+
+static void dpram_dump_memory(struct link_device *ld, char *buff)
+{
+ struct dpram_link_device *dpld = to_dpram_link_device(ld);
+ u8 __iomem *base = dpld->dpctl->dp_base;
+ u32 size = dpld->dpctl->dp_size;
+
+ dpram_wake_up(dpld);
+ memcpy(buff, base, size);
+}
+
+static int dpram_dump_start(struct link_device *ld, struct io_device *iod)
+{
+ struct dpram_link_device *dpld = to_dpram_link_device(ld);
+
+ if (dpld->ext_op && dpld->ext_op->dump_start)
+ return dpld->ext_op->dump_start(dpld);
+ else
+ return -ENODEV;
+}
+
+static int dpram_dump_update(struct link_device *ld, struct io_device *iod,
+ unsigned long arg)
+{
+ struct dpram_link_device *dpld = to_dpram_link_device(ld);
+
+ if (dpld->ext_op && dpld->ext_op->dump_update)
+ return dpld->ext_op->dump_update(dpld, (void *)arg);
+ else
+ return -ENODEV;
+}
+
+static int dpram_ioctl(struct link_device *ld, struct io_device *iod,
+ unsigned int cmd, unsigned long arg)
+{
+ struct dpram_link_device *dpld = to_dpram_link_device(ld);
+ int err = 0;
+
+/*
+ mif_info("%s: cmd 0x%08X\n", ld->name, cmd);
+*/
+
+ switch (cmd) {
+ case IOCTL_DPRAM_INIT_STATUS:
+ mif_debug("%s: get dpram init status\n", ld->name);
+ return dpld->dpram_init_status;
+
+ default:
+ if (dpld->ext_ioctl) {
+ err = dpld->ext_ioctl(dpld, iod, cmd, arg);
+ } else {
+ mif_err("%s: ERR! invalid cmd 0x%08X\n", ld->name, cmd);
+ err = -EINVAL;
+ }
+
+ break;
+ }
+
+ return err;
+}
+
+static int dpram_table_init(struct dpram_link_device *dpld)
+{
+ struct link_device *ld = &dpld->ld;
+ u8 __iomem *dp_base;
+ int i;
+
+ if (!dpld->dp_base) {
+ mif_info("%s: ERR! dpld->dp_base == NULL\n", ld->name);
+ return -EINVAL;
+ }
+ dp_base = dpld->dp_base;
+
+ /* Map for IPC */
+ if (dpld->dpctl->ipc_map) {
+ memcpy(&dpld->ipc_map, dpld->dpctl->ipc_map,
+ sizeof(struct dpram_ipc_map));
+ }
+
+ dpld->magic_ap2cp = dpld->ipc_map.magic_ap2cp;
+ dpld->access_ap2cp = dpld->ipc_map.access_ap2cp;
+
+ dpld->magic_cp2ap = dpld->ipc_map.magic_cp2ap;
+ dpld->access_cp2ap = dpld->ipc_map.access_cp2ap;
+
+ dpld->address_buffer = dpld->ipc_map.address_buffer;
+
+ for (i = 0; i < dpld->max_ipc_dev; i++)
+ dpld->dev[i] = &dpld->ipc_map.dev[i];
+ dpld->mbx2ap = dpld->ipc_map.mbx_cp2ap;
+ dpld->mbx2cp = dpld->ipc_map.mbx_ap2cp;
+
+ /* Map for booting */
+ if (dpld->ext_op && dpld->ext_op->init_boot_map) {
+ dpld->ext_op->init_boot_map(dpld);
+ } else {
+ dpld->bt_map.magic = (u32 *)(dp_base);
+ dpld->bt_map.buff = (u8 *)(dp_base + DP_BOOT_BUFF_OFFSET);
+ dpld->bt_map.size = dpld->dp_size - 8;
+ }
+
+ /* Map for download (FOTA, UDL, etc.) */
+ if (dpld->ext_op && dpld->ext_op->init_dl_map) {
+ dpld->ext_op->init_dl_map(dpld);
+ } else {
+ dpld->dl_map.magic = (u32 *)(dp_base);
+ dpld->dl_map.buff = (u8 *)(dp_base + DP_DLOAD_BUFF_OFFSET);
+ }
+
+ /* Map for upload mode */
+ if (dpld->ext_op && dpld->ext_op->init_ul_map) {
+ dpld->ext_op->init_ul_map(dpld);
+ } else {
+ dpld->ul_map.magic = (u32 *)(dp_base);
+ dpld->ul_map.buff = (u8 *)(dp_base + DP_ULOAD_BUFF_OFFSET);
+ }
+
+ return 0;
+}
+
+static void dpram_setup_common_op(struct dpram_link_device *dpld)
+{
+ dpld->clear_intr = clear_intr;
+ dpld->recv_intr = recv_intr;
+ dpld->send_intr = send_intr;
+ dpld->get_magic = get_magic;
+ dpld->set_magic = set_magic;
+ dpld->get_access = get_access;
+ dpld->set_access = set_access;
+ dpld->get_tx_head = get_tx_head;
+ dpld->get_tx_tail = get_tx_tail;
+ dpld->set_tx_head = set_tx_head;
+ dpld->set_tx_tail = set_tx_tail;
+ dpld->get_tx_buff = get_tx_buff;
+ dpld->get_tx_buff_size = get_tx_buff_size;
+ dpld->get_rx_head = get_rx_head;
+ dpld->get_rx_tail = get_rx_tail;
+ dpld->set_rx_head = set_rx_head;
+ dpld->set_rx_tail = set_rx_tail;
+ dpld->get_rx_buff = get_rx_buff;
+ dpld->get_rx_buff_size = get_rx_buff_size;
+ dpld->get_mask_req_ack = get_mask_req_ack;
+ dpld->get_mask_res_ack = get_mask_res_ack;
+ dpld->get_mask_send = get_mask_send;
+}
+
+static int dpram_link_init(struct link_device *ld, struct io_device *iod)
+{
+ return 0;
+}
+
+static void dpram_link_terminate(struct link_device *ld, struct io_device *iod)
+{
+ return;
+}
+
+struct link_device *pld_create_link_device(struct platform_device *pdev)
+{
+ struct modem_data *mdm_data = NULL;
+ struct dpram_link_device *dpld = NULL;
+ struct link_device *ld = NULL;
+ struct resource *res = NULL;
+ resource_size_t res_size;
+ struct modemlink_dpram_control *dpctl = NULL;
+ unsigned long task_data;
+ int ret = 0;
+ int i = 0;
+ int bsize;
+ int qsize;
+
+ /* Get the platform data */
+ mdm_data = (struct modem_data *)pdev->dev.platform_data;
+ if (!mdm_data) {
+ mif_info("ERR! mdm_data == NULL\n");
+ goto err;
+ }
+ mif_info("modem = %s\n", mdm_data->name);
+ mif_info("link device = %s\n", mdm_data->link_name);
+
+ if (!mdm_data->dpram_ctl) {
+ mif_info("ERR! mdm_data->dpram_ctl == NULL\n");
+ goto err;
+ }
+ dpctl = mdm_data->dpram_ctl;
+
+ /* Alloc DPRAM link device structure */
+ dpld = kzalloc(sizeof(struct dpram_link_device), GFP_KERNEL);
+ if (!dpld) {
+ mif_info("ERR! kzalloc dpld fail\n");
+ goto err;
+ }
+ ld = &dpld->ld;
+
+ /* Retrieve modem data and DPRAM control data from the modem data */
+ ld->mdm_data = mdm_data;
+ ld->name = mdm_data->link_name;
+ ld->ipc_version = mdm_data->ipc_version;
+
+ /* Retrieve the most basic data for IPC from the modem data */
+ dpld->dpctl = dpctl;
+ dpld->dp_type = dpctl->dp_type;
+
+ if (mdm_data->ipc_version < SIPC_VER_50) {
+ if (!dpctl->max_ipc_dev) {
+ mif_info("ERR! no max_ipc_dev\n");
+ goto err;
+ }
+
+ ld->aligned = dpctl->aligned;
+ dpld->max_ipc_dev = dpctl->max_ipc_dev;
+ } else {
+ ld->aligned = 1;
+ dpld->max_ipc_dev = MAX_SIPC5_DEV;
+ }
+
+ /* Set attributes as a link device */
+ ld->init_comm = dpram_link_init;
+ ld->terminate_comm = dpram_link_terminate;
+ ld->send = dpram_send;
+ ld->force_dump = dpram_force_dump;
+ ld->dump_start = dpram_dump_start;
+ ld->dump_update = dpram_dump_update;
+ ld->ioctl = dpram_ioctl;
+
+ INIT_LIST_HEAD(&ld->list);
+
+ skb_queue_head_init(&ld->sk_fmt_tx_q);
+ skb_queue_head_init(&ld->sk_raw_tx_q);
+ skb_queue_head_init(&ld->sk_rfs_tx_q);
+ ld->skb_txq[IPC_FMT] = &ld->sk_fmt_tx_q;
+ ld->skb_txq[IPC_RAW] = &ld->sk_raw_tx_q;
+ ld->skb_txq[IPC_RFS] = &ld->sk_rfs_tx_q;
+
+ /* Set up function pointers */
+ dpram_setup_common_op(dpld);
+ dpld->dpram_dump = dpram_dump_memory;
+ dpld->ext_op = dpram_get_ext_op(mdm_data->modem_type);
+ if (dpld->ext_op && dpld->ext_op->ioctl)
+ dpld->ext_ioctl = dpld->ext_op->ioctl;
+
+ /* Retrieve DPRAM resource */
+ if (!dpctl->dp_base) {
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ mif_info("%s: ERR! platform_get_resource fail\n",
+ ld->name);
+ goto err;
+ }
+ res_size = resource_size(res);
+
+ dpctl->dp_base = ioremap_nocache(res->start, res_size);
+ dpctl->dp_size = res_size;
+ }
+ dpld->dp_base = dpctl->dp_base;
+ dpld->dp_size = dpctl->dp_size;
+
+ mif_info("%s: dp_type %d, aligned %d, dp_base 0x%08X, dp_size %d\n",
+ ld->name, dpld->dp_type, ld->aligned, (int)dpld->dp_base,
+ dpld->dp_size);
+
+ /* Initialize DPRAM map (physical map -> logical map) */
+ ret = dpram_table_init(dpld);
+ if (ret < 0) {
+ mif_info("%s: ERR! dpram_table_init fail (err %d)\n",
+ ld->name, ret);
+ goto err;
+ }
+
+ spin_lock_init(&dpld->pld_lock);
+
+ /* Disable IPC */
+ set_magic(dpld, 0);
+ set_access(dpld, 0);
+
+ dpld->dpram_init_status = DPRAM_INIT_STATE_NONE;
+
+ /* Initialize locks, completions, and bottom halves */
+ snprintf(dpld->wlock_name, DP_MAX_NAME_LEN, "%s_wlock", ld->name);
+ wake_lock_init(&dpld->wlock, WAKE_LOCK_SUSPEND, dpld->wlock_name);
+
+ init_completion(&dpld->dpram_init_cmd);
+ init_completion(&dpld->modem_pif_init_done);
+ init_completion(&dpld->udl_start_complete);
+ init_completion(&dpld->udl_cmd_complete);
+ init_completion(&dpld->dump_start_complete);
+ init_completion(&dpld->dump_recv_done);
+
+ task_data = (unsigned long)dpld;
+ tasklet_init(&dpld->rx_tsk, dpram_ipc_rx_task, task_data);
+
+ /* Prepare SKB queue head for RX processing */
+ for (i = 0; i < dpld->max_ipc_dev; i++)
+ skb_queue_head_init(&dpld->skb_rxq[i]);
+
+ /* Prepare RXB queue */
+ qsize = DPRAM_MAX_RXBQ_SIZE;
+ for (i = 0; i < dpld->max_ipc_dev; i++) {
+ bsize = rxbq_get_page_size(get_rx_buff_size(dpld, i));
+ dpld->rxbq[i].size = qsize;
+ dpld->rxbq[i].in = 0;
+ dpld->rxbq[i].out = 0;
+ dpld->rxbq[i].rxb = rxbq_create_pool(bsize, qsize);
+ if (!dpld->rxbq[i].rxb) {
+ mif_info("%s: ERR! %s rxbq_create_pool fail\n",
+ ld->name, get_dev_name(i));
+ goto err;
+ }
+ mif_info("%s: %s rxbq_pool created (bsize:%d, qsize:%d)\n",
+ ld->name, get_dev_name(i), bsize, qsize);
+ }
+
+ /* Prepare a multi-purpose miscellaneous buffer */
+ dpld->buff = kzalloc(dpld->dp_size, GFP_KERNEL);
+ if (!dpld->buff) {
+ mif_info("%s: ERR! kzalloc dpld->buff fail\n", ld->name);
+ goto err;
+ }
+
+ /* Retrieve DPRAM IRQ GPIO# */
+ dpld->gpio_dpram_int = mdm_data->gpio_dpram_int;
+
+ /* Retrieve DPRAM IRQ# */
+ if (!dpctl->dpram_irq) {
+ dpctl->dpram_irq = platform_get_irq_byname(pdev, "dpram_irq");
+ if (dpctl->dpram_irq < 0) {
+ mif_info("%s: ERR! platform_get_irq_byname fail\n",
+ ld->name);
+ goto err;
+ }
+ }
+ dpld->irq = dpctl->dpram_irq;
+
+ /* Retrieve DPRAM IRQ flags */
+ if (!dpctl->dpram_irq_flags)
+ dpctl->dpram_irq_flags = (IRQF_NO_SUSPEND | IRQF_TRIGGER_LOW);
+ dpld->irq_flags = dpctl->dpram_irq_flags;
+
+ /* Register DPRAM interrupt handler */
+ snprintf(dpld->irq_name, DP_MAX_NAME_LEN, "%s_irq", ld->name);
+ ret = dpram_register_isr(dpld->irq, dpram_irq_handler, dpld->irq_flags,
+ dpld->irq_name, dpld);
+ if (ret)
+ goto err;
+
+ return ld;
+
+err:
+ if (dpld) {
+ kfree(dpld->buff);
+ kfree(dpld);
+ }
+
+ return NULL;
+}
+
diff --git a/drivers/misc/modem_if/modem_link_device_pld.h b/drivers/misc/modem_if/modem_link_device_pld.h
new file mode 100644
index 0000000..2656110
--- /dev/null
+++ b/drivers/misc/modem_if/modem_link_device_pld.h
@@ -0,0 +1,532 @@
+/*
+ * Copyright (C) 2011 Google, Inc.
+ * Copyright (C) 2010 Samsung Electronics.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef __MODEM_LINK_DEVICE_DPRAM_H__
+#define __MODEM_LINK_DEVICE_DPRAM_H__
+
+#include <linux/spinlock.h>
+#include <linux/wakelock.h>
+#include <linux/workqueue.h>
+#include <linux/timer.h>
+#include <linux/platform_data/modem.h>
+
+#include "modem_prj.h"
+
+#define DPRAM_MAGIC_CODE 0xAA
+
+/* interrupt masks.*/
+#define INT_MASK_VALID 0x0080
+#define INT_MASK_CMD 0x0040
+#define INT_VALID(x) ((x) & INT_MASK_VALID)
+#define INT_CMD_VALID(x) ((x) & INT_MASK_CMD)
+#define INT_NON_CMD(x) (INT_MASK_VALID | (x))
+#define INT_CMD(x) (INT_MASK_VALID | INT_MASK_CMD | (x))
+
+#define EXT_UDL_MASK 0xF000
+#define EXT_UDL_CMD(x) ((x) & EXT_UDL_MASK)
+#define EXT_INT_VALID_MASK 0x8000
+#define EXT_CMD_VALID_MASK 0x4000
+#define UDL_CMD_VALID_MASK 0x2000
+#define EXT_INT_VALID(x) ((x) & EXT_INT_VALID_MASK)
+#define EXT_CMD_VALID(x) ((x) & EXT_CMD_VALID_MASK)
+#define UDL_CMD_VALID(x) ((x) & UDL_CMD_VALID_MASK)
+#define INT_EXT_CMD(x) (EXT_INT_VALID_MASK | EXT_CMD_VALID_MASK | (x))
+
+#define EXT_CMD_MASK(x) ((x) & 0x0FFF)
+#define EXT_CMD_SET_SPEED_LOW 0x0011
+#define EXT_CMD_SET_SPEED_MID 0x0012
+#define EXT_CMD_SET_SPEED_HIGH 0x0013
+
+#define UDL_RESULT_SUCCESS 0x1
+#define UDL_RESULT_FAIL 0x2
+
+#define UDL_CMD_MASK(x) (((x) >> 8) & 0xF)
+#define UDL_CMD_RECV_READY 0x1
+#define UDL_CMD_DL_START_REQ 0x2
+#define UDL_CMD_DL_START_RESP 0x3
+#define UDL_CMD_IMAGE_SEND_REQ 0x4
+#define UDL_CMD_SEND_DONE_RESP 0x5
+#define UDL_CMD_SEND_DONE_REQ 0x6
+#define UDL_CMD_UPDATE_DONE 0x7
+#define UDL_CMD_STATUS_UPDATE 0x8
+#define UDL_CMD_IMAGE_SEND_RESP 0x9
+#define UDL_CMD_EFS_CLEAR_RESP 0xB
+#define UDL_CMD_ALARM_BOOT_OK 0xC
+#define UDL_CMD_ALARM_BOOT_FAIL 0xD
+
+#define CMD_IMG_START_REQ 0x9200
+#define CMD_IMG_SEND_REQ 0x9400
+#define CMD_DL_SEND_DONE_REQ 0x9600
+#define CMD_UL_RECV_RESP 0x9601
+#define CMD_UL_RECV_DONE_RESP 0x9801
+
+/* special interrupt cmd indicating modem boot failure. */
+#define INT_POWERSAFE_FAIL 0xDEAD
+
+#define INT_MASK_REQ_ACK_RFS 0x0400 /* Request RES_ACK_RFS */
+#define INT_MASK_RES_ACK_RFS 0x0200 /* Response of REQ_ACK_RFS */
+#define INT_MASK_SEND_RFS 0x0100 /* Indicate sending RFS data */
+
+#define INT_MASK_REQ_ACK_F 0x0020
+#define INT_MASK_REQ_ACK_R 0x0010
+#define INT_MASK_RES_ACK_F 0x0008
+#define INT_MASK_RES_ACK_R 0x0004
+#define INT_MASK_SEND_F 0x0002
+#define INT_MASK_SEND_R 0x0001
+
+#define INT_MASK_REQ_ACK_RFS 0x0400 /* Request RES_ACK_RFS */
+#define INT_MASK_RES_ACK_RFS 0x0200 /* Response of REQ_ACK_RFS */
+#define INT_MASK_SEND_RFS 0x0100 /* Indicate sending RFS data */
+
+#define INT_MASK_RES_ACK_SET \
+ (INT_MASK_RES_ACK_F | INT_MASK_RES_ACK_R | INT_MASK_RES_ACK_RFS)
+
+#define INT_MASK_SEND_SET \
+ (INT_MASK_SEND_F | INT_MASK_SEND_R | INT_MASK_SEND_RFS)
+
+#define INT_CMD_MASK(x) ((x) & 0xF)
+#define INT_CMD_INIT_START 0x1
+#define INT_CMD_INIT_END 0x2
+#define INT_CMD_REQ_ACTIVE 0x3
+#define INT_CMD_RES_ACTIVE 0x4
+#define INT_CMD_REQ_TIME_SYNC 0x5
+#define INT_CMD_CRASH_RESET 0x7
+#define INT_CMD_PHONE_START 0x8
+#define INT_CMD_ERR_DISPLAY 0x9
+#define INT_CMD_CRASH_EXIT 0x9
+#define INT_CMD_CP_DEEP_SLEEP 0xA
+#define INT_CMD_NV_REBUILDING 0xB
+#define INT_CMD_EMER_DOWN 0xC
+#define INT_CMD_PIF_INIT_DONE 0xD
+#define INT_CMD_SILENT_NV_REBUILDING 0xE
+#define INT_CMD_NORMAL_PWR_OFF 0xF
+
+#define START_FLAG 0x7F
+#define END_FLAG 0x7E
+
+#define DP_MAGIC_DMDL 0x4445444C
+#define DP_MAGIC_UMDL 0x4445444D
+#define DP_DPRAM_SIZE 0x4000
+#define DP_DEFAULT_WRITE_LEN 8168
+#define DP_DEFAULT_DUMP_LEN 16128
+#define DP_DUMP_HEADER_SIZE 7
+
+#define UDL_TIMEOUT (50 * HZ)
+#define UDL_SEND_TIMEOUT (200 * HZ)
+#define FORCE_CRASH_ACK_TIMEOUT (5 * HZ)
+#define DUMP_TIMEOUT (30 * HZ)
+#define DUMP_START_TIMEOUT (100 * HZ)
+#define DUMP_WAIT_TIMEOUT (HZ >> 10) /* 1/1024 second */
+
+#define PLD_ADDR_MASK(x) (0x00003FFF & (unsigned long)(x))
+
+enum host_boot_mode {
+ HOST_BOOT_MODE_NORMAL,
+ HOST_BOOT_MODE_DUMP,
+};
+
+enum dpram_init_status {
+ DPRAM_INIT_STATE_NONE,
+ DPRAM_INIT_STATE_READY,
+};
+
+struct dpram_boot_img {
+ char *addr;
+ int size;
+ enum host_boot_mode mode;
+ unsigned req;
+ unsigned resp;
+};
+
+#define MAX_PAYLOAD_SIZE 0x2000
+struct dpram_boot_frame {
+ unsigned req; /* AP->CP request */
+ unsigned resp; /* response expected by AP */
+ ssize_t len; /* data size in the buffer */
+ unsigned offset; /* offset to write into DPRAM */
+ char data[MAX_PAYLOAD_SIZE];
+};
+
+/* buffer type for modem image */
+struct dpram_dump_arg {
+ char *buff; /* pointer to the buffer */
+ int buff_size; /* buffer size */
+ unsigned req; /* AP->CP request */
+ unsigned resp; /* CP->AP response */
+ bool cmd; /* AP->CP command */
+};
+
+struct dpram_firmware {
+ char *firmware;
+ int size;
+ int is_delta;
+};
+enum dpram_link_mode {
+ DPRAM_LINK_MODE_INVALID = 0,
+ DPRAM_LINK_MODE_IPC,
+ DPRAM_LINK_MODE_BOOT,
+ DPRAM_LINK_MODE_DLOAD,
+ DPRAM_LINK_MODE_ULOAD,
+};
+
+struct dpram_boot_map {
+ u32 __iomem *magic;
+ u8 __iomem *buff;
+ u32 __iomem *req;
+ u32 __iomem *resp;
+ u32 size;
+};
+
+struct qc_dpram_boot_map {
+ u8 __iomem *buff;
+ u16 __iomem *frame_size;
+ u16 __iomem *tag;
+ u16 __iomem *count;
+};
+
+struct dpram_dload_map {
+ u32 __iomem *magic;
+ u8 __iomem *buff;
+};
+
+struct dpram_uload_map {
+ u32 __iomem *magic;
+ u8 __iomem *buff;
+};
+
+struct dpram_ota_header {
+ u8 start_index;
+ u16 nframes;
+ u16 curframe;
+ u16 len;
+
+} __packed;
+
+struct ul_header {
+ u8 bop;
+ u16 total_frame;
+ u16 curr_frame;
+ u16 len;
+} __packed;
+
+struct dpram_udl_param {
+ unsigned char *addr;
+ unsigned int size;
+ unsigned int count;
+ unsigned int tag;
+};
+
+struct dpram_udl_check {
+ unsigned int total_size;
+ unsigned int rest_size;
+ unsigned int send_size;
+ unsigned int copy_start;
+ unsigned int copy_complete;
+ unsigned int boot_complete;
+};
+
+#define DP_BOOT_BUFF_OFFSET 4
+#define DP_DLOAD_BUFF_OFFSET 4
+#define DP_ULOAD_BUFF_OFFSET 4
+#define DP_BOOT_REQ_OFFSET 0
+#define DP_BOOT_RESP_OFFSET 8
+
+#define MAX_WQ_NAME_LENGTH 64
+
+#define DPRAM_MAX_RXBQ_SIZE 256
+
+struct dpram_rxb {
+ u8 *buff;
+ unsigned size;
+
+ u8 *data;
+ unsigned len;
+};
+
+struct dpram_rxb_queue {
+ int size;
+ int in;
+ int out;
+ struct dpram_rxb *rxb;
+};
+
+/*
+ mbx_ap2cp + 0x0
+ magic_code +
+ access_enable +
+ padding +
+ mbx_cp2ap + 0x1000
+ magic_code +
+ access_enable +
+ padding +
+ fmt_tx_head + fmt_tx_tail + fmt_tx_buff + 0x2000
+ raw_tx_head + raw_tx_tail + raw_tx_buff +
+ fmt_rx_head + fmt_rx_tail + fmt_rx_buff + 0x3000
+ raw_rx_head + raw_rx_tail + raw_rx_buff +
+ = 2 +
+ 4094 +
+ 2 +
+ 4094 +
+ 2 +
+ 2 +
+ 2 + 2 + 1020 +
+ 2 + 2 + 3064 +
+ 2 + 2 + 1020 +
+ 2 + 2 + 3064
+ */
+
+#define DP_PLD_FMT_TX_BUFF_SZ 1024
+#define DP_PLD_RAW_TX_BUFF_SZ 3072
+#define DP_PLD_FMT_RX_BUFF_SZ 1024
+#define DP_PLD_RAW_RX_BUFF_SZ 3072
+
+#define MAX_MSM_EDPRAM_IPC_DEV 2 /* FMT, RAW */
+
+struct dpram_ipc_pld_map {
+ u16 mbx_ap2cp;
+ u16 magic_ap2cp;
+ u16 access_ap2cp;
+ u16 fmt_tx_head;
+ u16 raw_tx_head;
+ u16 fmt_rx_tail;
+ u16 raw_rx_tail;
+ u16 temp1;
+ u8 padding1[4080];
+
+ u16 mbx_cp2ap;
+ u16 magic_cp2ap;
+ u16 access_cp2ap;
+ u16 fmt_tx_tail;
+ u16 raw_tx_tail;
+ u16 fmt_rx_head;
+ u16 raw_rx_head;
+ u16 temp2;
+ u8 padding2[4080];
+
+ u8 fmt_tx_buff[DP_PLD_FMT_TX_BUFF_SZ];
+ u8 raw_tx_buff[DP_PLD_RAW_TX_BUFF_SZ];
+ u8 fmt_rx_buff[DP_PLD_RAW_TX_BUFF_SZ];
+ u8 raw_rx_buff[DP_PLD_RAW_RX_BUFF_SZ];
+
+ u8 padding3[16384];
+
+ u16 address_buffer;
+};
+
+/*
+ magic_code +
+ access_enable +
+ fmt_tx_head + fmt_tx_tail + fmt_tx_buff +
+ raw_tx_head + raw_tx_tail + raw_tx_buff +
+ fmt_rx_head + fmt_rx_tail + fmt_rx_buff +
+ raw_rx_head + raw_rx_tail + raw_rx_buff +
+ mbx_cp2ap +
+ mbx_ap2cp
+ = 2 +
+ 2 +
+ 2 + 2 + 1336 +
+ 2 + 2 + 4564 +
+ 2 + 2 + 1336 +
+ 2 + 2 + 9124 +
+ 2 +
+ 2
+ = 16384
+*/
+#define DP_16K_FMT_TX_BUFF_SZ 1336
+#define DP_16K_RAW_TX_BUFF_SZ 4564
+#define DP_16K_FMT_RX_BUFF_SZ 1336
+#define DP_16K_RAW_RX_BUFF_SZ 9124
+
+struct dpram_ipc_16k_map {
+ u16 magic;
+ u16 access;
+
+ u16 fmt_tx_head;
+ u16 fmt_tx_tail;
+ u8 fmt_tx_buff[DP_16K_FMT_TX_BUFF_SZ];
+
+ u16 raw_tx_head;
+ u16 raw_tx_tail;
+ u8 raw_tx_buff[DP_16K_RAW_TX_BUFF_SZ];
+
+ u16 fmt_rx_head;
+ u16 fmt_rx_tail;
+ u8 fmt_rx_buff[DP_16K_FMT_RX_BUFF_SZ];
+
+ u16 raw_rx_head;
+ u16 raw_rx_tail;
+ u8 raw_rx_buff[DP_16K_RAW_RX_BUFF_SZ];
+
+ u16 mbx_cp2ap;
+ u16 mbx_ap2cp;
+};
+
+#define DP_MAX_NAME_LEN 32
+
+struct dpram_ext_op;
+
+struct dpram_link_device {
+ struct link_device ld;
+
+ /* The mode of this DPRAM link device */
+ enum dpram_link_mode mode;
+
+ /* DPRAM address and size */
+ u8 __iomem *dp_base; /* DPRAM base virtual address */
+ u32 dp_size; /* DPRAM size */
+ enum dpram_type dp_type; /* DPRAM type */
+
+ /* DPRAM IRQ GPIO# */
+ unsigned gpio_dpram_int;
+
+ /* DPRAM IRQ from CP */
+ int irq;
+ unsigned long irq_flags;
+ char irq_name[DP_MAX_NAME_LEN];
+
+ /* Link to DPRAM control functions dependent on each platform */
+ int max_ipc_dev;
+ struct modemlink_dpram_control *dpctl;
+
+ /* Physical configuration -> logical configuration */
+ union {
+ struct dpram_boot_map bt_map;
+ struct qc_dpram_boot_map qc_bt_map;
+ };
+
+ struct dpram_dload_map dl_map;
+ struct dpram_uload_map ul_map;
+
+ /* IPC device map */
+ struct dpram_ipc_map ipc_map;
+
+ /* Pointers (aliases) to IPC device map */
+ u16 __iomem *magic_ap2cp;
+ u16 __iomem *access_ap2cp;
+ u16 __iomem *magic_cp2ap;
+ u16 __iomem *access_cp2ap;
+ u16 __iomem *address_buffer;
+
+ struct dpram_ipc_device *dev[MAX_IPC_DEV];
+ u16 __iomem *mbx2ap;
+ u16 __iomem *mbx2cp;
+
+ /* Wakelock for DPRAM device */
+ struct wake_lock wlock;
+ char wlock_name[DP_MAX_NAME_LEN];
+
+ /* For booting */
+ unsigned boot_start_complete;
+ struct completion dpram_init_cmd;
+ struct completion modem_pif_init_done;
+
+ /* For UDL */
+ struct tasklet_struct ul_tsk;
+ struct tasklet_struct dl_tsk;
+ struct completion udl_start_complete;
+ struct completion udl_cmd_complete;
+ struct dpram_udl_check udl_check;
+ struct dpram_udl_param udl_param;
+
+ /* For CP RAM dump */
+ struct timer_list crash_ack_timer;
+ struct completion dump_start_complete;
+ struct completion dump_recv_done;
+ struct timer_list dump_timer;
+ int dump_rcvd; /* Count of dump packets received */
+
+ /* For locking TX process */
+ spinlock_t tx_rx_lock;
+ spinlock_t pld_lock;
+
+ /* For efficient RX process */
+ struct tasklet_struct rx_tsk;
+ struct dpram_rxb_queue rxbq[MAX_IPC_DEV];
+ struct io_device *iod[MAX_IPC_DEV];
+ bool use_skb;
+ struct sk_buff_head skb_rxq[MAX_IPC_DEV];
+
+ /* For retransmission after buffer full state */
+ atomic_t res_required[MAX_IPC_DEV];
+
+ /* For wake-up/sleep control */
+ atomic_t accessing;
+
+ /* Multi-purpose miscellaneous buffer */
+ u8 *buff;
+
+ /* DPRAM IPC initialization status */
+ int dpram_init_status;
+
+ /* Alias to device-specific IOCTL function */
+ int (*ext_ioctl)(struct dpram_link_device *dpld, struct io_device *iod,
+ unsigned int cmd, unsigned long arg);
+
+ /* For DPRAM dump */
+ void (*dpram_dump)(struct link_device *ld, char *buff);
+
+ /* Common operations for each DPRAM */
+ void (*clear_intr)(struct dpram_link_device *dpld);
+ u16 (*recv_intr)(struct dpram_link_device *dpld);
+ void (*send_intr)(struct dpram_link_device *dpld, u16 mask);
+ u16 (*get_magic)(struct dpram_link_device *dpld);
+ void (*set_magic)(struct dpram_link_device *dpld, u16 value);
+ u16 (*get_access)(struct dpram_link_device *dpld);
+ void (*set_access)(struct dpram_link_device *dpld, u16 value);
+ u32 (*get_tx_head)(struct dpram_link_device *dpld, int id);
+ u32 (*get_tx_tail)(struct dpram_link_device *dpld, int id);
+ void (*set_tx_head)(struct dpram_link_device *dpld, int id, u32 head);
+ void (*set_tx_tail)(struct dpram_link_device *dpld, int id, u32 tail);
+ u8 *(*get_tx_buff)(struct dpram_link_device *dpld, int id);
+ u32 (*get_tx_buff_size)(struct dpram_link_device *dpld, int id);
+ u32 (*get_rx_head)(struct dpram_link_device *dpld, int id);
+ u32 (*get_rx_tail)(struct dpram_link_device *dpld, int id);
+ void (*set_rx_head)(struct dpram_link_device *dpld, int id, u32 head);
+ void (*set_rx_tail)(struct dpram_link_device *dpld, int id, u32 tail);
+ u8 *(*get_rx_buff)(struct dpram_link_device *dpld, int id);
+ u32 (*get_rx_buff_size)(struct dpram_link_device *dpld, int id);
+ u16 (*get_mask_req_ack)(struct dpram_link_device *dpld, int id);
+ u16 (*get_mask_res_ack)(struct dpram_link_device *dpld, int id);
+ u16 (*get_mask_send)(struct dpram_link_device *dpld, int id);
+
+ /* Extended operations for various modems */
+ struct dpram_ext_op *ext_op;
+};
+
+/* converts from struct link_device* to struct xxx_link_device* */
+#define to_dpram_link_device(linkdev) \
+ container_of(linkdev, struct dpram_link_device, ld)
+
+struct dpram_ext_op {
+ int exist;
+
+ void (*init_boot_map)(struct dpram_link_device *dpld);
+ void (*init_dl_map)(struct dpram_link_device *dpld);
+ void (*init_ul_map)(struct dpram_link_device *dpld);
+
+ int (*dload_bin)(struct dpram_link_device *dpld, struct sk_buff *skb);
+ void (*dload_cmd_handler)(struct dpram_link_device *dpld, u16 cmd);
+
+ void (*cp_start_handler)(struct dpram_link_device *dpld);
+
+ void (*crash_log)(struct dpram_link_device *dpld);
+ int (*dump_start)(struct dpram_link_device *dpld);
+ int (*dump_update)(struct dpram_link_device *dpld, void *arg);
+
+ int (*ioctl)(struct dpram_link_device *dpld, struct io_device *iod,
+ unsigned int cmd, unsigned long arg);
+};
+
+struct dpram_ext_op *dpram_get_ext_op(enum modem_t modem);
+#endif
diff --git a/drivers/misc/modem_if/modem_link_device_pld_ext_op.c b/drivers/misc/modem_if/modem_link_device_pld_ext_op.c
new file mode 100644
index 0000000..ae6578c
--- /dev/null
+++ b/drivers/misc/modem_if/modem_link_device_pld_ext_op.c
@@ -0,0 +1,556 @@
+/*
+ * Copyright (C) 2010 Samsung Electronics.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/time.h>
+#include <linux/interrupt.h>
+#include <linux/timer.h>
+#include <linux/wakelock.h>
+#include <linux/delay.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/vmalloc.h>
+#include <linux/if_arp.h>
+#include <linux/platform_device.h>
+#include <linux/kallsyms.h>
+#include <linux/platform_data/modem.h>
+
+#include "modem_prj.h"
+#include "modem_link_device_pld.h"
+#include "modem_utils.h"
+
+#if defined(CONFIG_CDMA_MODEM_MDM6600) || defined(CONFIG_GSM_MODEM_ESC6270)
+enum qc_dload_tag {
+ QC_DLOAD_TAG_NONE = 0,
+ QC_DLOAD_TAG_BIN,
+ QC_DLOAD_TAG_NV,
+ QC_DLOAD_TAG_MAX
+};
+
+static void qc_dload_task(unsigned long data);
+
+static void qc_init_boot_map(struct dpram_link_device *dpld)
+{
+ struct qc_dpram_boot_map *bt_map = &dpld->qc_bt_map;
+ struct modemlink_dpram_control *dpctl = dpld->dpctl;
+
+ bt_map->buff = dpld->dev[0]->txq.buff;
+ bt_map->frame_size = (u16 *)(dpld->dp_base + dpctl->boot_size_offset);
+ bt_map->tag = (u16 *)(dpld->dp_base + dpctl->boot_tag_offset);
+ bt_map->count = (u16 *)(dpld->dp_base + dpctl->boot_count_offset);
+
+ tasklet_init(&dpld->dl_tsk, qc_dload_task, (unsigned long)dpld);
+}
+
+static void qc_dload_map(struct dpram_link_device *dpld, u8 is_upload)
+{
+ struct qc_dpram_boot_map *bt_map = &dpld->qc_bt_map;
+ struct modemlink_dpram_control *dpctl = dpld->dpctl;
+ unsigned int upload_offset = 0;
+
+ if (is_upload == 1) {
+ upload_offset = 0x1000;
+ bt_map->buff = dpld->dev[0]->rxq.buff;
+ } else {
+ upload_offset = 0;
+ bt_map->buff = dpld->dev[0]->txq.buff;
+ }
+
+ bt_map->frame_size = (u16 *)(dpld->dp_base +
+ dpctl->boot_size_offset + upload_offset);
+ bt_map->tag = (u16 *)(dpld->dp_base +
+ dpctl->boot_tag_offset + upload_offset);
+ bt_map->count = (u16 *)(dpld->dp_base +
+ dpctl->boot_count_offset + upload_offset);
+
+}
+
+static int qc_prepare_download(struct dpram_link_device *dpld)
+{
+ int retval = 0;
+ int count = 0;
+
+ qc_dload_map(dpld, 0);
+
+ while (1) {
+ if (dpld->udl_check.copy_start) {
+ dpld->udl_check.copy_start = 0;
+ break;
+ }
+
+ msleep(20);
+
+ count++;
+ if (count > 1000) {
+ mif_err("ERR! count %d\n", count);
+ return -1;
+ }
+ }
+
+ return retval;
+}
+
+static void _qc_do_download(struct dpram_link_device *dpld,
+ struct dpram_udl_param *param)
+{
+ struct qc_dpram_boot_map *bt_map = &dpld->qc_bt_map;
+
+ if (param->size <= dpld->dpctl->max_boot_frame_size) {
+ iowrite16(PLD_ADDR_MASK(&bt_map->buff[0]),
+ dpld->address_buffer);
+ memcpy(dpld->dp_base, param->addr, param->size);
+
+ iowrite16(PLD_ADDR_MASK(&bt_map->frame_size[0]),
+ dpld->address_buffer);
+ iowrite16(param->size, dpld->dp_base);
+
+ iowrite16(PLD_ADDR_MASK(&bt_map->tag[0]),
+ dpld->address_buffer);
+ iowrite16(param->tag, dpld->dp_base);
+
+ iowrite16(PLD_ADDR_MASK(&bt_map->count[0]),
+ dpld->address_buffer);
+ iowrite16(param->count, dpld->dp_base);
+
+ dpld->send_intr(dpld, 0xDB12);
+ } else {
+ mif_info("param->size %d\n", param->size);
+ }
+}
+
+static int _qc_download(struct dpram_link_device *dpld, void *arg,
+ enum qc_dload_tag tag)
+{
+ int retval = 0;
+ int count = 0;
+ int cnt_limit;
+ unsigned char *img;
+ struct dpram_udl_param param;
+
+ retval = copy_from_user((void *)&param, (void *)arg, sizeof(param));
+ if (retval < 0) {
+ mif_err("ERR! copy_from_user fail\n");
+ return -1;
+ }
+
+ img = vmalloc(param.size);
+ if (!img) {
+ mif_err("ERR! vmalloc fail\n");
+ return -1;
+ }
+ memset(img, 0, param.size);
+ memcpy(img, param.addr, param.size);
+
+ dpld->udl_check.total_size = param.size;
+ dpld->udl_check.rest_size = param.size;
+ dpld->udl_check.send_size = 0;
+ dpld->udl_check.copy_complete = 0;
+
+ dpld->udl_param.addr = img;
+ dpld->udl_param.size = dpld->dpctl->max_boot_frame_size;
+ if (tag == QC_DLOAD_TAG_NV)
+ dpld->udl_param.count = 1;
+ else
+ dpld->udl_param.count = param.count;
+ dpld->udl_param.tag = tag;
+
+ if (dpld->udl_check.rest_size < dpld->dpctl->max_boot_frame_size)
+ dpld->udl_param.size = dpld->udl_check.rest_size;
+
+ /* Download image (binary or NV) */
+ _qc_do_download(dpld, &dpld->udl_param);
+
+ /* Wait for completion
+ */
+ if (tag == QC_DLOAD_TAG_NV)
+ cnt_limit = 200;
+ else
+ cnt_limit = 1000;
+
+ while (1) {
+ if (dpld->udl_check.copy_complete) {
+ dpld->udl_check.copy_complete = 0;
+ retval = 0;
+ break;
+ }
+
+ msleep(20);
+
+ count++;
+ if (count > cnt_limit) {
+ mif_err("ERR! count %d\n", count);
+ retval = -1;
+ break;
+ }
+ }
+
+ vfree(img);
+
+ return retval;
+}
+
+static int qc_download_bin(struct dpram_link_device *dpld, void *arg)
+{
+ return _qc_download(dpld, arg, QC_DLOAD_TAG_BIN);
+}
+
+static int qc_download_nv(struct dpram_link_device *dpld, void *arg)
+{
+ return _qc_download(dpld, arg, QC_DLOAD_TAG_NV);
+}
+
+static void qc_dload_task(unsigned long data)
+{
+ struct dpram_link_device *dpld = (struct dpram_link_device *)data;
+
+ dpld->udl_check.send_size += dpld->udl_param.size;
+ dpld->udl_check.rest_size -= dpld->udl_param.size;
+
+ dpld->udl_param.addr += dpld->udl_param.size;
+
+ if (dpld->udl_check.send_size >= dpld->udl_check.total_size) {
+ dpld->udl_check.copy_complete = 1;
+ dpld->udl_param.tag = 0;
+ return;
+ }
+
+ if (dpld->udl_check.rest_size < dpld->dpctl->max_boot_frame_size)
+ dpld->udl_param.size = dpld->udl_check.rest_size;
+
+ dpld->udl_param.count += 1;
+
+ _qc_do_download(dpld, &dpld->udl_param);
+}
+
+static void qc_dload_cmd_handler(struct dpram_link_device *dpld, u16 cmd)
+{
+ switch (cmd) {
+ case 0x1234:
+ dpld->udl_check.copy_start = 1;
+ break;
+
+ case 0xDBAB:
+ tasklet_schedule(&dpld->dl_tsk);
+ break;
+
+ case 0xABCD:
+ mif_info("[%s] booting Start\n", dpld->ld.name);
+ dpld->udl_check.boot_complete = 1;
+ break;
+
+ default:
+ mif_err("ERR! unknown command 0x%04X\n", cmd);
+ }
+}
+
+static int qc_boot_start(struct dpram_link_device *dpld)
+{
+ u16 mask = 0;
+ int count = 0;
+
+ /* Send interrupt -> '0x4567' */
+ mask = 0x4567;
+ dpld->send_intr(dpld, mask);
+
+ while (1) {
+ if (dpld->udl_check.boot_complete) {
+ dpld->udl_check.boot_complete = 0;
+ break;
+ }
+
+ msleep(20);
+
+ count++;
+ if (count > 200) {
+ mif_err("ERR! count %d\n", count);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int qc_boot_post_process(struct dpram_link_device *dpld)
+{
+ int count = 0;
+
+ while (1) {
+ if (dpld->boot_start_complete) {
+ dpld->boot_start_complete = 0;
+ break;
+ }
+
+ msleep(20);
+
+ count++;
+ if (count > 200) {
+ mif_err("ERR! count %d\n", count);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static void qc_start_handler(struct dpram_link_device *dpld)
+{
+ /*
+ * INT_MASK_VALID | INT_MASK_CMD | INT_MASK_CP_AIRPLANE_BOOT |
+ * INT_MASK_CP_AP_ANDROID | INT_MASK_CMD_INIT_END
+ */
+ u16 mask = (0x0080 | 0x0040 | 0x1000 | 0x0100 | 0x0002);
+
+ dpld->boot_start_complete = 1;
+
+ /* Send INIT_END code to CP */
+ mif_info("send 0x%04X (INIT_END)\n", mask);
+
+ dpld->send_intr(dpld, mask);
+}
+
+static void qc_crash_log(struct dpram_link_device *dpld)
+{
+ struct link_device *ld = &dpld->ld;
+ static unsigned char buf[151];
+ u8 __iomem *data = NULL;
+
+ data = dpld->get_rx_buff(dpld, IPC_FMT);
+ memcpy(buf, data, (sizeof(buf) - 1));
+
+ mif_info("PHONE ERR MSG\t| %s Crash\n", ld->mdm_data->name);
+ mif_info("PHONE ERR MSG\t| %s\n", buf);
+}
+
+static int _qc_data_upload(struct dpram_link_device *dpld,
+ struct dpram_udl_param *param)
+{
+ struct qc_dpram_boot_map *bt_map = &dpld->qc_bt_map;
+ int retval = 0;
+ u16 intval = 0;
+ int count = 0;
+
+ while (1) {
+ if (!gpio_get_value(dpld->gpio_dpram_int)) {
+ intval = dpld->recv_intr(dpld);
+ if (intval == 0xDBAB) {
+ break;
+ } else {
+ mif_err("intr 0x%08x\n", intval);
+ return -1;
+ }
+ }
+
+ msleep(20);
+
+ count++;
+ if (count > 200) {
+ mif_err("<%s:%d>\n", __func__, __LINE__);
+ return -1;
+ }
+ }
+
+ iowrite16(PLD_ADDR_MASK(&bt_map->frame_size[0]),
+ dpld->address_buffer);
+ param->size = ioread16(dpld->dp_base);
+
+ iowrite16(PLD_ADDR_MASK(&bt_map->tag[0]),
+ dpld->address_buffer);
+ param->tag = ioread16(dpld->dp_base);
+
+ iowrite16(PLD_ADDR_MASK(&bt_map->count[0]),
+ dpld->address_buffer);
+ param->count = ioread16(dpld->dp_base);
+
+ iowrite16(PLD_ADDR_MASK(&bt_map->buff[0]),
+ dpld->address_buffer);
+ memcpy(param->addr, dpld->dp_base, param->size);
+
+ dpld->send_intr(dpld, 0xDB12);
+
+ return retval;
+}
+
+static int qc_uload_step1(struct dpram_link_device *dpld)
+{
+ int retval = 0;
+ int count = 0;
+ u16 intval = 0;
+ u16 mask = 0;
+
+ qc_dload_map(dpld, 1);
+
+ mif_info("+---------------------------------------------+\n");
+ mif_info("| UPLOAD PHONE SDRAM |\n");
+ mif_info("+---------------------------------------------+\n");
+
+ while (1) {
+ if (!gpio_get_value(dpld->gpio_dpram_int)) {
+ intval = dpld->recv_intr(dpld);
+ mif_info("intr 0x%04x\n", intval);
+ if (intval == 0x1234) {
+ break;
+ } else {
+ mif_info("ERR! invalid intr\n");
+ return -1;
+ }
+ }
+
+ msleep(20);
+
+ count++;
+ if (count > 200) {
+ intval = dpld->recv_intr(dpld);
+ mif_info("count %d, intr 0x%04x\n", count, intval);
+ if (intval == 0x1234)
+ break;
+ return -1;
+ }
+ }
+
+ mask = 0xDEAD;
+ dpld->send_intr(dpld, mask);
+
+ return retval;
+}
+
+static int qc_uload_step2(struct dpram_link_device *dpld, void *arg)
+{
+ int retval = 0;
+ struct dpram_udl_param param;
+
+ retval = copy_from_user((void *)&param, (void *)arg, sizeof(param));
+ if (retval < 0) {
+ mif_err("ERR! copy_from_user fail (err %d)\n", retval);
+ return -1;
+ }
+
+ retval = _qc_data_upload(dpld, &param);
+ if (retval < 0) {
+ mif_err("ERR! _qc_data_upload fail (err %d)\n", retval);
+ return -1;
+ }
+
+ if (!(param.count % 500))
+ mif_info("param->count = %d\n", param.count);
+
+ if (param.tag == 4) {
+ enable_irq(dpld->irq);
+ mif_info("param->tag = %d\n", param.tag);
+ }
+
+ retval = copy_to_user((unsigned long *)arg, &param, sizeof(param));
+ if (retval < 0) {
+ mif_err("ERR! copy_to_user fail (err %d)\n", retval);
+ return -1;
+ }
+
+ return retval;
+}
+
+static int qc_ioctl(struct dpram_link_device *dpld, struct io_device *iod,
+ unsigned int cmd, unsigned long arg)
+{
+ struct link_device *ld = &dpld->ld;
+ int err = 0;
+
+ switch (cmd) {
+ case IOCTL_DPRAM_PHONE_POWON:
+ err = qc_prepare_download(dpld);
+ if (err < 0)
+ mif_info("%s: ERR! prepare_download fail\n", ld->name);
+ break;
+
+ case IOCTL_DPRAM_PHONEIMG_LOAD:
+ err = qc_download_bin(dpld, (void *)arg);
+ if (err < 0)
+ mif_info("%s: ERR! download_bin fail\n", ld->name);
+ break;
+
+ case IOCTL_DPRAM_NVDATA_LOAD:
+ err = qc_download_nv(dpld, (void *)arg);
+ if (err < 0)
+ mif_info("%s: ERR! download_nv fail\n", ld->name);
+ break;
+
+ case IOCTL_DPRAM_PHONE_BOOTSTART:
+ err = qc_boot_start(dpld);
+ if (err < 0) {
+ mif_info("%s: ERR! boot_start fail\n", ld->name);
+ break;
+ }
+
+ err = qc_boot_post_process(dpld);
+ if (err < 0)
+ mif_info("%s: ERR! boot_post_process fail\n", ld->name);
+
+ break;
+
+ case IOCTL_DPRAM_PHONE_UPLOAD_STEP1:
+ disable_irq_nosync(dpld->irq);
+ err = qc_uload_step1(dpld);
+ if (err < 0) {
+ enable_irq(dpld->irq);
+ mif_info("%s: ERR! upload_step1 fail\n", ld->name);
+ }
+ break;
+
+ case IOCTL_DPRAM_PHONE_UPLOAD_STEP2:
+ err = qc_uload_step2(dpld, (void *)arg);
+ if (err < 0) {
+ enable_irq(dpld->irq);
+ mif_info("%s: ERR! upload_step2 fail\n", ld->name);
+ }
+ break;
+
+ default:
+ mif_err("%s: ERR! invalid cmd 0x%08X\n", ld->name, cmd);
+ err = -EINVAL;
+ break;
+ }
+
+ return err;
+}
+#endif
+
+static struct dpram_ext_op ext_op_set[] = {
+#if defined(CONFIG_CDMA_MODEM_MDM6600)
+ [QC_MDM6600] = {
+ .exist = 1,
+ .init_boot_map = qc_init_boot_map,
+ .dload_cmd_handler = qc_dload_cmd_handler,
+ .cp_start_handler = qc_start_handler,
+ .crash_log = qc_crash_log,
+ .ioctl = qc_ioctl,
+ },
+#endif
+#if defined(CONFIG_GSM_MODEM_ESC6270)
+ [QC_ESC6270] = {
+ .exist = 1,
+ .init_boot_map = qc_init_boot_map,
+ .dload_cmd_handler = qc_dload_cmd_handler,
+ .cp_start_handler = qc_start_handler,
+ .crash_log = qc_crash_log,
+ .ioctl = qc_ioctl,
+ },
+#endif
+};
+
+struct dpram_ext_op *dpram_get_ext_op(enum modem_t modem)
+{
+ if (ext_op_set[modem].exist)
+ return &ext_op_set[modem];
+ else
+ return NULL;
+}
diff --git a/drivers/misc/modem_if/modem_link_device_spi.c b/drivers/misc/modem_if/modem_link_device_spi.c
new file mode 100644
index 0000000..1ea5a2c
--- /dev/null
+++ b/drivers/misc/modem_if/modem_link_device_spi.c
@@ -0,0 +1,1795 @@
+/*
+ * Copyright (C) 2011 Google, Inc.
+ * Copyright (C) 2010 Samsung Electronics.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/time.h>
+#include <linux/interrupt.h>
+#include <linux/timer.h>
+#include <linux/wakelock.h>
+#include <linux/delay.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/vmalloc.h>
+#include <linux/if_arp.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/kthread.h>
+
+#include <linux/platform_data/modem.h>
+#include "modem_prj.h"
+#include "modem_link_device_spi.h"
+#include "modem_utils.h"
+
+/* For function which has void parmeter */
+static struct spi_link_device *p_spild;
+static struct spi_device *p_spi;
+
+static void spi_send_work(int spi_sigs)
+{
+ struct spi_work_type *spi_wq = NULL;
+ spi_wq = kmalloc(sizeof(struct spi_work_type), GFP_ATOMIC);
+ spi_wq->signal_code = spi_sigs;
+ INIT_WORK(&spi_wq->work, spi_work);
+ queue_work(p_spild->spi_wq, (struct work_struct *)spi_wq);
+}
+
+static void spi_send_work_front(int spi_sigs)
+{
+ struct spi_work_type *spi_wq = NULL;
+ spi_wq = kmalloc(sizeof(struct spi_work_type), GFP_ATOMIC);
+ spi_wq->signal_code = spi_sigs;
+ INIT_WORK(&spi_wq->work, spi_work);
+ queue_work_front(p_spild->spi_wq, (struct work_struct *)spi_wq);
+}
+
+static irqreturn_t spi_srdy_irq_handler(int irq, void *p_ld)
+{
+ struct link_device *ld = (struct link_device *)p_ld;
+ struct spi_link_device *spild = to_spi_link_device(ld);
+
+ irqreturn_t result = IRQ_HANDLED;
+
+ if (!spild->boot_done)
+ return result;
+
+ if (!wake_lock_active(&spild->spi_wake_lock)
+ && spild->send_modem_spi != 1) {
+ wake_lock(&spild->spi_wake_lock);
+ pr_debug("[SPI] [%s](%d) spi_wakelock locked . spild->spi_state[%d]\n",
+ __func__, __LINE__, (int)spild->spi_state);
+ }
+
+ if (spild->send_modem_spi == 1)
+ up(&spild->srdy_sem);
+
+ /* SRDY interrupt work on SPI_STATE_IDLE state for receive data */
+ if (spild->spi_state == SPI_STATE_IDLE
+ || spild->spi_state == SPI_STATE_RX_TERMINATE
+ || spild->spi_state == SPI_STATE_TX_TERMINATE) {
+ spi_send_work_front(SPI_WORK_RECEIVE);
+
+ return result;
+ }
+
+ return result;
+}
+
+static irqreturn_t spi_subsrdy_irq_handler(int irq, void *p_ld)
+{
+ struct link_device *ld = (struct link_device *)p_ld;
+ struct spi_link_device *spild = to_spi_link_device(ld);
+
+ irqreturn_t result = IRQ_HANDLED;
+
+ /* SRDY interrupt work on SPI_STATE_TX_WAIT state for send data */
+ if (spild->spi_state == SPI_STATE_TX_WAIT)
+ return result;
+
+ pr_debug("%s spild->spi_state[%d]\n",
+ "[SPI] spi_main_subsrdy_rising_handler :",
+ (int)spild->spi_state);
+
+
+ return result;
+}
+
+static int spi_send
+(
+ struct link_device *ld,
+ struct io_device *iod,
+ struct sk_buff *skb
+)
+{
+ struct sk_buff_head *txq;
+ enum dev_format fmt = iod->format;
+
+ u32 data;
+ u32 cmd_ready = 0x12341234;
+ u32 cmd_start = 0x45674567;
+ int ret;
+
+ switch (fmt) {
+ case IPC_FMT:
+ case IPC_RAW:
+ case IPC_RFS:
+ txq = ld->skb_txq[fmt];
+ skb_queue_tail(txq, skb);
+
+ ret = skb->len;
+
+ break;
+
+ case IPC_BOOT:
+ if (get_user(data, (u32 __user *)skb->data))
+ return -EFAULT;
+
+ if (data == cmd_ready) {
+ p_spild->ril_send_modem_img = 1;
+ p_spild->ril_send_cnt = 0;
+ } else if (data == cmd_start) {
+ p_spild->ril_send_modem_img = 0;
+ if (!queue_work(p_spild->ipc_spi_wq,
+ &p_spild->send_modem_w))
+ pr_err("(%d) already exist w-q\n",
+ __LINE__);
+ } else {
+ if (p_spild->ril_send_modem_img) {
+ memcpy((void *)(p_spild->p_virtual_buff
+ + p_spild->ril_send_cnt),
+ skb->data, skb->len);
+ p_spild->ril_send_cnt += skb->len;
+ }
+ }
+ ret = skb->len;
+ dev_kfree_skb_any(skb);
+
+ return ret;
+
+ default:
+ pr_err("[LNK/E] <%s:%s> No TXQ for %s\n",
+ __func__, ld->name, iod->name);
+ dev_kfree_skb_any(skb);
+ return 0;
+ }
+
+ spi_send_work(SPI_WORK_SEND);
+
+ return ret;
+}
+
+
+static int spi_register_isr
+(
+ unsigned irq,
+ irqreturn_t (*isr)(int, void*),
+ unsigned long flag,
+ const char *name,
+ struct link_device *ld
+)
+{
+ int ret = 0;
+
+ ret = request_irq(irq, isr, flag, name, ld);
+ if (ret) {
+ pr_err("[LNK/E] <%s> request_irq fail (%d)\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = enable_irq_wake(irq);
+ if (ret)
+ pr_err("[LNK/E] <%s> enable_irq_wake fail (%d)\n",
+ __func__, ret);
+
+ pr_debug("[LNK] <%s> IRQ#%d handler is registered.\n", __func__, irq);
+
+ return 0;
+}
+
+void spi_unregister_isr(unsigned irq, void *data)
+{
+ free_irq(irq, data);
+}
+
+int spi_tx_rx_sync(u8 *tx_d, u8 *rx_d, unsigned len)
+{
+ struct spi_transfer t;
+ struct spi_message msg;
+
+ memset(&t, 0, sizeof t);
+
+ t.len = len;
+
+ t.tx_buf = tx_d;
+ t.rx_buf = rx_d;
+
+ t.cs_change = 0;
+
+ t.bits_per_word = 32;
+ t.speed_hz = 12000000;
+
+ spi_message_init(&msg);
+ spi_message_add_tail(&t, &msg);
+
+ return spi_sync(p_spi, &msg);
+}
+
+static int spi_buff_write
+(
+ struct spi_link_device *spild,
+ int dev_id,
+ const char *buff,
+ unsigned size
+)
+{
+ u32 templength, buf_length;
+ u32 spi_data_mux;
+ u32 spi_packet_free_length;
+
+ u8 *spi_packet;
+ struct spi_data_packet_header *spi_packet_header;
+
+ spi_packet_header = (struct spi_data_packet_header *)spild->buff;
+ spi_packet = (char *)spild->buff;
+
+ spi_packet_free_length = SPI_DATA_PACKET_MAX_PACKET_BODY_SIZE -
+ spi_packet_header->current_data_size;
+
+ buf_length = size + SPI_DATA_MUX_SIZE + SPI_DATA_LENGTH_SIZE;
+
+ /* not enough space in spi packet */
+ if (spi_packet_free_length < buf_length) {
+ spi_packet_header->more = 1;
+ return 0;
+ }
+
+ /* check spi mux type */
+ switch (dev_id) {
+ case IPC_FMT:
+ spi_data_mux = SPI_DATA_MUX_IPC;
+ break;
+
+ case IPC_RAW:
+ spi_data_mux = SPI_DATA_MUX_RAW;
+ break;
+
+ case IPC_RFS:
+ spi_data_mux = SPI_DATA_MUX_RFS;
+ break;
+
+ default:
+ pr_err("%s %s\n",
+ "[SPI] ERROR : spi_buff_write:",
+ "invalid dev_id");
+ return 0;
+ }
+
+ /* copy spi mux field */
+ memcpy(spi_packet + SPI_DATA_PACKET_HEADER_SIZE +
+ spi_packet_header->current_data_size,
+ &spi_data_mux, SPI_DATA_MUX_SIZE);
+ spi_packet_header->current_data_size += SPI_DATA_MUX_SIZE;
+
+ /* copy spi data length field */
+ templength = size-SPI_DATA_BOF_SIZE-SPI_DATA_EOF_SIZE;
+ memcpy(spi_packet + SPI_DATA_PACKET_HEADER_SIZE +
+ spi_packet_header->current_data_size,
+ &templength, SPI_DATA_LENGTH_SIZE);
+ spi_packet_header->current_data_size += SPI_DATA_LENGTH_SIZE;
+
+ /* copy data field */
+ memcpy(spi_packet + SPI_DATA_PACKET_HEADER_SIZE +
+ spi_packet_header->current_data_size,
+ buff, size);
+ spi_packet_header->current_data_size += size;
+
+ return buf_length;
+}
+
+
+int spi_prepare_tx_packet(void)
+{
+ struct link_device *ld;
+ struct spi_link_device *spild;
+ struct sk_buff *skb;
+ int ret;
+ int i;
+
+ spild = p_spild;
+ ld = &spild->ld;
+
+ for (i = 0; i < spild->max_ipc_dev; i++) {
+ while ((skb = skb_dequeue(ld->skb_txq[i]))) {
+ if (ld->mode == LINK_MODE_IPC) {
+ ret = spi_buff_write(spild, i,
+ skb->data, skb->len);
+ if (!ret) {
+ skb_queue_head(ld->skb_txq[i], skb);
+ break;
+ }
+ } else {
+ pr_err("[LNK/E] <%s:%s> "
+ "ld->mode != LINK_MODE_IPC\n",
+ __func__, ld->name);
+ }
+ dev_kfree_skb_any(skb);
+ }
+ }
+
+ return 1;
+}
+
+
+static int spi_start_data_send(void)
+{
+ struct link_device *ld;
+ struct spi_link_device *spild;
+ int i;
+
+ spild = p_spild;
+ ld = &spild->ld;
+
+ for (i = 0; i < spild->max_ipc_dev; i++) {
+ if (skb_queue_len(ld->skb_txq[i]) > 0) {
+ spi_send_work(SPI_WORK_SEND);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static void spi_tx_work(void)
+{
+ struct spi_link_device *spild;
+ struct io_device *iod;
+ char *spi_packet_buf;
+ char *spi_sync_buf;
+
+ spild = p_spild;
+
+ /* check SUB SRDY state */
+ if (gpio_get_value(spild->gpio_ipc_sub_srdy) ==
+ SPI_GPIOLEVEL_HIGH) {
+ spi_start_data_send();
+ return;
+ }
+
+ /* check SRDY state */
+ if (gpio_get_value(spild->gpio_ipc_srdy) ==
+ SPI_GPIOLEVEL_HIGH) {
+ spi_start_data_send();
+ return;
+ }
+
+ if (get_console_suspended())
+ return;
+
+ if (spild->spi_state == SPI_STATE_END)
+ return;
+
+ /* change state SPI_STATE_IDLE to SPI_STATE_TX_WAIT */
+ spild->spi_state = SPI_STATE_TX_WAIT;
+ spild->spi_timer_tx_state = SPI_STATE_TIME_START;
+
+ gpio_set_value(spild->gpio_ipc_mrdy, SPI_GPIOLEVEL_HIGH);
+
+ /* Start TX timer */
+ spild->spi_tx_timer.expires =
+ jiffies + ((SPI_TIMER_TX_WAIT_TIME * HZ) / 1000);
+ add_timer(&spild->spi_tx_timer);
+ /* check SUBSRDY state */
+ while (gpio_get_value(spild->gpio_ipc_sub_srdy) ==
+ SPI_GPIOLEVEL_LOW) {
+ if (spild->spi_timer_tx_state == SPI_STATE_TIME_OVER) {
+ pr_err("%s spild->spi_state=[%d]\n",
+ "[spi_tx_work] == spi Fail to receiving SUBSRDY CONF :"
+ , (int)spild->spi_state);
+
+ spild->spi_timer_tx_state = SPI_STATE_TIME_START;
+
+ gpio_set_value(spild->gpio_ipc_mrdy,
+ SPI_GPIOLEVEL_LOW);
+
+ /* change state SPI_STATE_TX_WAIT */
+ /* to SPI_STATE_IDLE */
+ spild->spi_state = SPI_STATE_IDLE;
+ spi_send_work(SPI_WORK_SEND);
+
+ return;
+ }
+ }
+ /* Stop TX timer */
+ del_timer(&spild->spi_tx_timer);
+
+ if (spild->spi_state != SPI_STATE_START
+ && spild->spi_state != SPI_STATE_END
+ && spild->spi_state != SPI_STATE_INIT) {
+ spi_packet_buf = spild->buff;
+ spi_sync_buf = spild->sync_buff;
+
+ gpio_set_value(spild->gpio_ipc_sub_mrdy, SPI_GPIOLEVEL_HIGH);
+ gpio_set_value(spild->gpio_ipc_mrdy, SPI_GPIOLEVEL_LOW);
+
+ /* change state SPI_STATE_TX_WAIT to
+ SPI_MAIN_STATE_TX_SENDING */
+ spild->spi_state = SPI_STATE_TX_SENDING;
+
+ memset(spi_packet_buf, 0, SPI_MAX_PACKET_SIZE);
+ memset(spi_sync_buf, 0, SPI_MAX_PACKET_SIZE);
+
+ spi_prepare_tx_packet();
+
+ if (spi_tx_rx_sync((void *)spi_packet_buf,
+ (void *)spi_sync_buf,
+ SPI_MAX_PACKET_SIZE) != 0) {
+ /* TODO: save failed packet */
+ /* back data to each queue */
+ pr_err("[SPI] spi_dev_send fail\n");
+
+ /* add cp reset when spi sync fail */
+ iod = link_get_iod_with_format(&spild->ld, IPC_FMT);
+
+ if (iod)
+ iod->modem_state_changed(iod,
+ STATE_CRASH_RESET);
+ }
+
+ spild->spi_state = SPI_STATE_TX_TERMINATE;
+
+ gpio_set_value(spild->gpio_ipc_sub_mrdy, SPI_GPIOLEVEL_LOW);
+
+ /* change state SPI_MAIN_STATE_TX_SENDING to SPI_STATE_IDLE */
+ spild->spi_state = SPI_STATE_IDLE;
+ spi_start_data_send();
+ } else
+ pr_err("[SPI] ERR : _start_packet_tx:spild->spi_state[%d]",
+ (int)spild->spi_state);
+
+ return;
+}
+
+int spi_buff_read(struct spi_link_device *spild)
+{
+ struct link_device *ld;
+ struct spi_data_packet_header *spi_packet_header;
+ struct sk_buff *skb;
+ char *spi_packet;
+ int dev_id;
+ unsigned int spi_packet_length;
+ unsigned int spi_packet_cur_pos = SPI_DATA_PACKET_HEADER_SIZE;
+
+ unsigned int spi_data_mux;
+ unsigned int spi_data_length;
+ unsigned int data_length;
+ u8 *spi_cur_data;
+ u8 *dst;
+
+ spi_packet = spild->buff;
+ ld = &spild->ld;
+
+ /* check spi packet header */
+ if (*(unsigned int *)spi_packet == 0x00000000
+ || *(unsigned int *)spi_packet == 0xFFFFFFFF) {
+ /* if spi header is invalid, */
+ /* read spi header again with next 4 byte */
+ spi_packet += SPI_DATA_PACKET_HEADER_SIZE;
+ }
+
+ /* read spi packet header */
+ spi_packet_header = (struct spi_data_packet_header *)spi_packet;
+ spi_packet_length = SPI_DATA_PACKET_HEADER_SIZE +
+ spi_packet_header->current_data_size;
+
+
+ do {
+ /* read spi data mux and set current queue */
+ memcpy(&spi_data_mux,
+ spi_packet + spi_packet_cur_pos, SPI_DATA_MUX_SIZE);
+
+ switch (spi_data_mux & SPI_DATA_MUX_NORMAL_MASK) {
+ case SPI_DATA_MUX_IPC:
+ dev_id = IPC_FMT;
+ break;
+
+ case SPI_DATA_MUX_RAW:
+ dev_id = IPC_RAW;
+ break;
+
+ case SPI_DATA_MUX_RFS:
+ dev_id = IPC_RFS;
+ break;
+
+ default:
+ pr_err("%s len[%u], pos[%u]\n",
+ "[SPI] ERROR : spi_buff_read : MUX error",
+ spi_packet_length, spi_packet_cur_pos);
+
+ return spi_packet_cur_pos -
+ SPI_DATA_PACKET_HEADER_SIZE;
+ }
+
+ /* read spi data length */
+ memcpy(&spi_data_length, spi_packet +
+ spi_packet_cur_pos + SPI_DATA_LENGTH_OFFSET,
+ SPI_DATA_LENGTH_SIZE);
+
+ data_length = spi_data_length + SPI_DATA_BOF_SIZE +
+ SPI_DATA_EOF_SIZE;
+
+ spi_data_length += SPI_DATA_HEADER_SIZE;
+
+ /* read data and make spi data */
+ spi_cur_data = spi_packet + spi_packet_cur_pos;
+
+ /* enqueue spi data */
+ skb = alloc_skb(data_length, GFP_ATOMIC);
+ if (!skb) {
+ pr_err("%s %s\n",
+ "[SPI] ERROR : spi_buff_read:",
+ "Can't allocate memory for SPI");
+ return 0;
+ }
+
+ dst = skb_put(skb, data_length);
+
+ memcpy(dst, spi_packet +
+ spi_packet_cur_pos + SPI_DATA_BOF_OFFSET,
+ data_length);
+
+ if (skb)
+ skb_queue_tail(&spild->skb_rxq[dev_id], skb);
+ else
+ pr_err("[LNK/E] <%s:%s> read[%d] fail\n",
+ __func__, ld->name, dev_id);
+
+ /* move spi packet current posision */
+ spi_packet_cur_pos += spi_data_length;
+ } while ((spi_packet_length - 1) > spi_packet_cur_pos);
+
+ return 1;
+}
+
+static void spi_rx_work(void)
+{
+ struct link_device *ld;
+ struct spi_link_device *spild;
+ struct sk_buff *skb;
+ struct io_device *iod;
+ char *spi_packet_buf;
+ char *spi_sync_buf;
+ int i;
+
+ spild = p_spild;
+ ld = &spild->ld;
+ if (!spild)
+ pr_err("[LNK/E] <%s> dpld == NULL\n", __func__);
+
+ if (!wake_lock_active(&spild->spi_wake_lock))
+ return;
+
+ if (gpio_get_value(spild->gpio_ipc_srdy) == SPI_GPIOLEVEL_LOW)
+ return;
+
+ if (get_console_suspended())
+ return;
+
+ if (spild->spi_state == SPI_STATE_END)
+ return;
+
+ spild->spi_state = SPI_STATE_RX_WAIT;
+ spild->spi_timer_rx_state = SPI_STATE_TIME_START;
+
+ gpio_set_value(spild->gpio_ipc_sub_mrdy, SPI_GPIOLEVEL_HIGH);
+ /* Start TX timer */
+ spild->spi_rx_timer.expires =
+ jiffies + ((SPI_TIMER_RX_WAIT_TIME * HZ) / 1000);
+ add_timer(&spild->spi_rx_timer);
+
+ /* check SUBSRDY state */
+ while (gpio_get_value(spild->gpio_ipc_sub_srdy) ==
+ SPI_GPIOLEVEL_LOW) {
+ if (spild->spi_timer_rx_state == SPI_STATE_TIME_OVER) {
+ pr_err("[SPI] ERROR(Failed MASTER RX:%d ms)",
+ SPI_TIMER_RX_WAIT_TIME);
+
+ spild->spi_timer_rx_state = SPI_STATE_TIME_START;
+
+ gpio_set_value(spild->gpio_ipc_sub_mrdy,
+ SPI_GPIOLEVEL_LOW);
+
+ /* change state SPI_MAIN_STATE_RX_WAIT */
+ /* to SPI_STATE_IDLE */
+ spild->spi_state = SPI_STATE_IDLE;
+
+ return;
+ }
+ }
+ /* Stop TX timer */
+ del_timer(&spild->spi_rx_timer);
+
+ if (spild->spi_state == SPI_STATE_START
+ || spild->spi_state == SPI_STATE_END
+ || spild->spi_state == SPI_STATE_INIT)
+ return;
+
+ spi_packet_buf = spild->buff;
+ spi_sync_buf = spild->sync_buff;
+
+ memset(spi_packet_buf, 0, SPI_MAX_PACKET_SIZE);
+ memset(spi_sync_buf, 0, SPI_MAX_PACKET_SIZE);
+
+ if (spi_tx_rx_sync((void *)spi_sync_buf, (void *)spi_packet_buf,
+ SPI_MAX_PACKET_SIZE) == 0) {
+ /* parsing SPI packet */
+ if (spi_buff_read(spild) > 0) {
+ /* call function for send data to IPC, RAW, RFS */
+ for (i = 0; i < spild->max_ipc_dev; i++) {
+ iod = spild->iod[i];
+ while ((skb = skb_dequeue(&spild->skb_rxq[i]))
+ != NULL) {
+ if (iod->recv(iod, ld, skb->data,
+ skb->len) < 0)
+ pr_err("[LNK/E] <%s:%s> recv fail\n",
+ __func__, ld->name);
+ dev_kfree_skb_any(skb);
+ }
+ }
+ }
+ } else {
+ pr_err("%s %s\n", "[SPI] ERROR : spi_rx_work :",
+ "spi sync failed");
+
+ /* add cp reset when spi sync fail */
+ iod = link_get_iod_with_format(&spild->ld, IPC_FMT);
+
+ if (iod)
+ iod->modem_state_changed(iod,
+ STATE_CRASH_RESET);
+ }
+
+ spild->spi_state = SPI_STATE_RX_TERMINATE;
+
+ gpio_set_value(spild->gpio_ipc_sub_mrdy, SPI_GPIOLEVEL_LOW);
+
+ /* change state SPI_MAIN_STATE_RX_WAIT to SPI_STATE_IDLE */
+ spild->spi_state = SPI_STATE_IDLE;
+ spi_start_data_send();
+}
+
+unsigned int sprd_crc_calc(char *buf_ptr, unsigned int len)
+{
+ unsigned int i;
+ unsigned short crc = 0;
+
+ while (len-- != 0) {
+ for (i = 0x80; i != 0 ; i = i>>1) {
+ if ((crc & 0x8000) != 0) {
+ crc = crc << 1 ;
+ crc = crc ^ 0x1021;
+ } else {
+ crc = crc << 1 ;
+ }
+
+ if ((*buf_ptr & i) != 0)
+ crc = crc ^ 0x1021;
+ }
+ buf_ptr++;
+ }
+
+ return crc;
+
+}
+
+unsigned short sprd_crc_calc_fdl(unsigned short *src, int len)
+{
+ unsigned int sum = 0;
+ unsigned short SourceValue, DestValue = 0;
+ unsigned short lowSourceValue, hiSourceValue = 0;
+
+ /* Get sum value of the source.*/
+ while (len > 1) {
+ SourceValue = *src++;
+ DestValue = 0;
+ lowSourceValue = (SourceValue & 0xFF00) >> 8;
+ hiSourceValue = (SourceValue & 0x00FF) << 8;
+ DestValue = lowSourceValue | hiSourceValue;
+
+ sum += DestValue;
+ len -= 2;
+ }
+
+ if (len == 1)
+ sum += *((unsigned char *) src);
+
+ sum = (sum >> 16) + (sum & 0x0FFFF);
+ sum += (sum >> 16);
+
+ return ~sum;
+}
+
+int encode_msg(struct sprd_image_buf *img, int bcrc)
+{
+ u16 crc; /* CRC value*/
+ u8 *src_ptr; /* source buffer pointer*/
+ int dest_len; /* output buffer length*/
+ u8 *dest_ptr; /* dest buffer pointer*/
+ u8 high_crc, low_crc = 0;
+ register int curr;
+
+ /* CRC Check. */
+ src_ptr = img->tx_b;
+
+ /* CRC Check. */
+ if (bcrc)
+ crc = sprd_crc_calc(src_ptr, img->tx_size);
+ else
+ crc = sprd_crc_calc_fdl
+ ((unsigned short *)src_ptr, img->tx_size);
+
+ high_crc = (crc>>8) & 0xFF;
+ low_crc = crc & 0xFF;
+
+ /* Get the total size to be allocated.*/
+ dest_len = 0;
+
+ for (curr = 0; curr < img->tx_size; curr++) {
+ switch (*(src_ptr+curr)) {
+ case HDLC_FLAG:
+ case HDLC_ESCAPE:
+ dest_len += 2;
+ break;
+ default:
+ dest_len++;
+ break;
+ }
+ }
+
+ switch (low_crc) {
+ case HDLC_FLAG:
+ case HDLC_ESCAPE:
+ dest_len += 2;
+ break;
+ default:
+ dest_len++;
+ }
+
+ switch (high_crc) {
+ case HDLC_FLAG:
+ case HDLC_ESCAPE:
+ dest_len += 2;
+ break;
+ default:
+ dest_len++;
+ }
+
+ dest_ptr = kmalloc(dest_len + 2, GFP_ATOMIC);
+ /* Memory Allocate fail.*/
+ if (dest_ptr == NULL)
+ return -ENOMEM;
+
+ *dest_ptr = HDLC_FLAG;
+ dest_len = 1;
+
+ /* do escape*/
+ for (curr = 0; curr < img->tx_size; curr++) {
+ switch (*(src_ptr+curr)) {
+ case HDLC_FLAG:
+ case HDLC_ESCAPE:
+ *(dest_ptr + dest_len++) = HDLC_ESCAPE;
+ *(dest_ptr + dest_len++) =
+ *(src_ptr + curr) ^ HDLC_ESCAPE_MASK;
+ break;
+ default:
+ *(dest_ptr + dest_len++) = *(src_ptr + curr);
+ break;
+ }
+ }
+
+ switch (high_crc) {
+ case HDLC_FLAG:
+ case HDLC_ESCAPE:
+ *(dest_ptr + dest_len++) = HDLC_ESCAPE;
+ *(dest_ptr + dest_len++) = high_crc ^ HDLC_ESCAPE_MASK;
+ break;
+ default:
+ *(dest_ptr + dest_len++) = high_crc;
+ }
+
+ switch (low_crc) {
+ case HDLC_FLAG:
+ case HDLC_ESCAPE:
+ *(dest_ptr + dest_len++) = HDLC_ESCAPE;
+ *(dest_ptr + dest_len++) = low_crc ^ HDLC_ESCAPE_MASK;
+ break;
+ default:
+ *(dest_ptr + dest_len++) = low_crc;
+ }
+
+
+ *(dest_ptr + dest_len++) = HDLC_FLAG;
+
+ memcpy(img->encoded_tx_b, dest_ptr, dest_len);
+ img->encoded_tx_size = dest_len;
+
+ kfree(dest_ptr);
+ return 0;
+}
+
+int decode_msg(struct sprd_image_buf *img, int bcrc)
+{
+ u16 crc; /* CRC value*/
+ u8 *src_ptr; /* source buffer pointer*/
+ int dest_len; /* output buffer length*/
+ u8 *dest_ptr; /* dest buffer pointer*/
+ register int curr;
+
+ /* Check if exist End Flag.*/
+ src_ptr = img->rx_b;
+
+ dest_len = 0;
+
+ if (img->rx_size < 4)
+ return -EINVAL;
+
+ /* Get the total size to be allocated for decoded message.*/
+ for (curr = 1; curr < img->rx_size - 1; curr++) {
+ switch (*(src_ptr + curr)) {
+ case HDLC_ESCAPE:
+ curr++;
+ dest_len++;
+ break;
+ default:
+ dest_len++;
+ break;
+ }
+ }
+
+ /* Allocate meomory for decoded message*/
+ dest_ptr = kmalloc(dest_len, GFP_ATOMIC);
+ /* Memory allocate fail.*/
+ if (dest_ptr == NULL)
+ return -ENOMEM;
+
+ memset(dest_ptr, 0, dest_len);
+
+ curr = 0;
+ dest_len = 0;
+ /* Do de-escape.*/
+ for (curr = 1; curr < img->rx_size - 1; curr++) {
+ switch (*(src_ptr + curr)) {
+ case HDLC_ESCAPE:
+ curr++;
+ *(dest_ptr + dest_len) =
+ *(src_ptr + curr) ^ HDLC_ESCAPE_MASK;
+ break;
+ default:
+ *(dest_ptr + dest_len) = *(src_ptr + curr);
+ break;
+ }
+
+ dest_len = dest_len + 1;
+ }
+
+ /* CRC Check. */
+ if (bcrc)
+ crc = sprd_crc_calc(dest_ptr, dest_len);
+ else
+ crc = sprd_crc_calc_fdl((unsigned short *)dest_ptr, dest_len);
+
+ if (crc != CRC_16_L_OK) {
+ pr_err("CRC error : 0x%X", crc);
+ kfree(dest_ptr);
+ return -EPERM;
+ }
+
+ memcpy(img->decoded_rx_b, dest_ptr, dest_len - CRC_CHECK_SIZE);
+ img->decoded_rx_size = dest_len - CRC_CHECK_SIZE ;
+
+ kfree(dest_ptr);
+ return 0;
+}
+
+static int spi_send_modem_bin_execute_cmd
+ (u8 *spi_ptr, u32 spi_size, u16 spi_type,
+ u16 spi_crc, struct sprd_image_buf *sprd_img)
+{
+ int i, retval;
+ u16 send_packet_size;
+ u8 *send_packet_data;
+ u16 d1_crc;
+ u16 d2_crc = spi_crc;
+ u16 type = spi_type;
+
+ /* D1 */
+ send_packet_size = spi_size; /* u32 -> u16 */
+ sprd_img->tx_size = 6;
+ M_16_SWAP(d2_crc);
+ memcpy(sprd_img->tx_b, &send_packet_size, sizeof(send_packet_size));
+ memcpy((sprd_img->tx_b+2), &type, sizeof(type));
+ memcpy((sprd_img->tx_b+4), &d2_crc, sizeof(d2_crc));
+
+ d1_crc = sprd_crc_calc_fdl
+ ((unsigned short *)sprd_img->tx_b, sprd_img->tx_size);
+ M_16_SWAP(d1_crc);
+ memcpy((sprd_img->tx_b+6), &d1_crc, sizeof(d1_crc));
+ sprd_img->tx_size += 2;
+
+ if (down_timeout(&p_spild->srdy_sem, 2 * HZ)) {
+ pr_err("(%d) SRDY TimeOUT!!! SRDY : %d, SEM : %d\n",
+ __LINE__,
+ gpio_get_value(p_spild->gpio_modem_bin_srdy),
+ p_spild->srdy_sem.count);
+ goto err;
+ }
+
+ retval = spi_tx_rx_sync
+ (sprd_img->tx_b, sprd_img->rx_b, sprd_img->tx_size);
+ if (retval != 0) {
+ pr_err("(%d) spi sync error : %d\n",
+ __LINE__, retval);
+ goto err;
+ }
+
+ if ((type == 0x0003) || (type == 0x0004)) {
+ pr_err("D2 Skip!!\n");
+ goto ACK;
+ }
+
+ /* D2 */
+ send_packet_data = spi_ptr;
+
+ if (down_timeout(&p_spild->srdy_sem, 2 * HZ)) {
+ pr_err("(%d) SRDY TimeOUT!!! SRDY : %d, SEM : %d\n", __LINE__,
+ gpio_get_value(p_spild->gpio_modem_bin_srdy),
+ p_spild->srdy_sem.count);
+ goto err;
+ }
+
+ retval = spi_tx_rx_sync
+ (send_packet_data, sprd_img->rx_b, send_packet_size);
+ if (retval != 0) {
+ pr_err("(%d) spi sync error : %d\n", __LINE__, retval);
+ goto err;
+ }
+
+ACK:
+
+ if (p_spild->is_cp_reset) {
+ while (!gpio_get_value(p_spild->gpio_modem_bin_srdy))
+ ;
+ } else {
+ if (down_timeout(&p_spild->srdy_sem, 2 * HZ)) {
+ pr_err("(%d) SRDY TimeOUT!!! SRDY : %d, SEM : %d\n",
+ __LINE__,
+ gpio_get_value(p_spild->gpio_modem_bin_srdy),
+ p_spild->srdy_sem.count);
+
+ pr_err("[SPI DUMP] TX_D1(%d) : [%02x %02x %02x %02x %02x %02x %02x %02x]\n",
+ sprd_img->tx_size, sprd_img->tx_b[0],
+ sprd_img->tx_b[1], sprd_img->tx_b[2],
+ sprd_img->tx_b[3], sprd_img->tx_b[4],
+ sprd_img->tx_b[5], sprd_img->tx_b[6],
+ sprd_img->tx_b[7]);
+ pr_err("[SPI DUMP] TX_D2(%d) :[%02x %02x %02x %02x %02x %02x %02x %02x]\n",
+ send_packet_size, spi_ptr[0], spi_ptr[1],
+ spi_ptr[2], spi_ptr[3], spi_ptr[4],
+ spi_ptr[5], spi_ptr[6], spi_ptr[7]);
+
+ /* WA (CP Reset) jongmoon.suh */
+ if (gpio_get_value(p_spild->gpio_modem_bin_srdy))
+ ;
+ else
+ goto err;
+
+ }
+ }
+
+ memset(sprd_img->tx_b, 0, SPRD_BLOCK_SIZE+10);
+ retval = spi_tx_rx_sync(sprd_img->tx_b, sprd_img->rx_b, 8);
+ if (retval != 0) {
+ pr_err("(%d) spi sync error : %d\n",
+ __LINE__, retval);
+ goto err;
+ }
+
+ memcpy(sprd_img->decoded_rx_b, sprd_img->rx_b, 4);
+
+ if ((*(sprd_img->decoded_rx_b+0) == 0x00) && \
+ (*(sprd_img->decoded_rx_b+1) == 0x80) && \
+ (*(sprd_img->decoded_rx_b+2) == 0x00) && \
+ (*(sprd_img->decoded_rx_b+3) == 0x00)) {
+ pr_debug("[SPRD] CP sent ACK");
+ } else {
+ pr_err("Transfer ACK error! srdy_sem = %d\n",
+ p_spild->srdy_sem.count);
+ pr_err("[SPI DUMP] RX(%d) : [ ", sprd_img->rx_size);
+ for (i = 0; i < 15; i++)
+ pr_err("%02x ", *((u8 *)(sprd_img->rx_b + i)));
+ pr_err("]");
+
+ goto err;
+ }
+
+ return retval;
+
+err:
+ return -EINVAL;
+}
+
+static int spi_send_modem_bin_xmit_img
+ (enum image_type type, struct image_buf *img)
+{
+ int retval = 0;
+ struct sprd_image_buf sprd_img;
+ unsigned int data_size;
+ unsigned int send_size = 0;
+ unsigned int rest_size = 0;
+ unsigned int spi_size = 0;
+ unsigned int address = 0;
+ unsigned int fdl1_size = 0;
+ /* No Translate */
+ u16 crc = 0;
+ u16 spi_type = 0;
+
+ unsigned char *spi_ptr;
+ unsigned char *ptr;
+ int i, j = 0;
+
+ u16 sprd_packet_size = SPRD_BLOCK_SIZE;
+
+ sprd_img.tx_b = kmalloc(SPRD_BLOCK_SIZE*2, GFP_ATOMIC);
+ if (!sprd_img.tx_b) {
+ pr_err("(%d) tx_b kmalloc fail.",
+ __LINE__);
+ return -ENOMEM;
+ }
+ memset(sprd_img.tx_b, 0, SPRD_BLOCK_SIZE*2);
+
+ sprd_img.rx_b = kmalloc(SPRD_BLOCK_SIZE*2, GFP_ATOMIC);
+ if (!sprd_img.rx_b) {
+ pr_err("(%d) rx_b kmalloc fail.",
+ __LINE__);
+ retval = -ENOMEM;
+ goto err3;
+ }
+ memset(sprd_img.rx_b, 0, SPRD_BLOCK_SIZE*2);
+
+ sprd_img.encoded_tx_b = kmalloc(SPRD_BLOCK_SIZE*2, GFP_ATOMIC);
+ if (!sprd_img.encoded_tx_b) {
+ pr_err("(%d) encoded_tx_b kmalloc fail.",
+ __LINE__);
+ retval = -ENOMEM;
+ goto err2;
+ }
+ memset(sprd_img.encoded_tx_b, 0, SPRD_BLOCK_SIZE*2);
+
+ sprd_img.decoded_rx_b = kmalloc(SPRD_BLOCK_SIZE*2, GFP_ATOMIC);
+ if (!sprd_img.decoded_rx_b) {
+ pr_err("(%d) encoded_rx_b kmalloc fail.",
+ __LINE__);
+ retval = -ENOMEM;
+ goto err1;
+ }
+ memset(sprd_img.decoded_rx_b, 0, SPRD_BLOCK_SIZE*2);
+
+ pr_debug("(%d) spi_send_modem_bin_xmit_img type : %d.\n",
+ __LINE__, type);
+ memcpy(&fdl1_size, (void *)(p_spild->p_virtual_buff + 4), 4);
+
+ switch (type) {
+ case MODEM_MAIN:
+ memcpy(&img->address,
+ (void *)(p_spild->p_virtual_buff + 8), 4);
+ memcpy(&img->length,
+ (void *)(p_spild->p_virtual_buff + 12), 4);
+ img->buf = (unsigned char *)
+ (p_spild->p_virtual_buff + 0x30 + fdl1_size);
+ img->offset = img->length + fdl1_size + 0x30;
+ pr_debug("(%d) spi_send_modem_bin_xmit_img save MAIN to img.\n",
+ __LINE__);
+
+ break;
+
+ case MODEM_DSP:
+ memcpy(&img->address,
+ (void *)(p_spild->p_virtual_buff + 16), 4);
+ memcpy(&img->length,
+ (void *)(p_spild->p_virtual_buff + 20), 4);
+ img->buf = (unsigned char *)
+ (p_spild->p_virtual_buff + img->offset);
+ img->offset += img->length;
+ pr_debug("(%d) spi_send_modem_bin_xmit_img save DSP to img.\n",
+ __LINE__);
+
+ break;
+
+ case MODEM_NV:
+ memcpy(&img->address,
+ (void *)(p_spild->p_virtual_buff + 24), 4);
+ memcpy(&img->length,
+ (void *)(p_spild->p_virtual_buff + 28), 4);
+ img->buf = (unsigned char *)
+ (p_spild->p_virtual_buff + img->offset);
+ img->offset += img->length;
+ pr_debug("(%d) spi_send_modem_bin_xmit_img save NV to img.\n",
+ __LINE__);
+
+ break;
+
+ case MODEM_EFS:
+ memcpy(&img->address,
+ (void *)(p_spild->p_virtual_buff + 32), 4);
+ memcpy(&img->length,
+ (void *)(p_spild->p_virtual_buff + 36), 4);
+ img->buf = (unsigned char *)
+ (p_spild->p_virtual_buff + img->offset);
+ img->offset += img->length;
+ pr_debug("(%d) spi_send_modem_bin_xmit_img save EFS to img.\n",
+ __LINE__);
+
+ break;
+ case MODEM_RUN:
+ memset(sprd_img.encoded_tx_b, 0, SPRD_BLOCK_SIZE*2);
+ sprd_img.encoded_tx_size = 0;
+ spi_type = 0x0004;
+ crc = 0;
+
+ spi_ptr = sprd_img.encoded_tx_b;
+ spi_size = sprd_img.encoded_tx_size;
+
+ retval = spi_send_modem_bin_execute_cmd
+ (spi_ptr, spi_size, spi_type, crc, &sprd_img);
+ if (retval < 0) {
+ pr_err("(%d) spi_send_modem_bin_execute_cmd fail : %d",
+ __LINE__, retval);
+ goto err0;
+ }
+ return retval;
+
+ default:
+ pr_err("(%d) spi_send_modem_bin_xmit_img wrong : %d.",
+ __LINE__, type);
+ goto err0;
+ }
+
+ pr_debug("(%d) Start send img. size : %d\n",
+ __LINE__, img->length);
+
+ ptr = img->buf;
+ data_size = sprd_packet_size;
+ rest_size = img->length;
+ address = img->address;
+
+ M_32_SWAP(img->address);
+ M_32_SWAP(img->length);
+
+ /* Send Transfer Start */
+ sprd_img.tx_size = 8;
+ memcpy((sprd_img.tx_b+0), &img->address, sizeof(img->address));
+ memcpy((sprd_img.tx_b+4), &img->length, sizeof(img->length));
+
+ spi_type = 0x0001;
+ crc = sprd_crc_calc_fdl
+ ((unsigned short *)sprd_img.tx_b, sprd_img.tx_size);
+ memcpy(sprd_img.encoded_tx_b, sprd_img.tx_b, sprd_img.tx_size);
+ sprd_img.encoded_tx_size = sprd_img.tx_size;
+
+ spi_ptr = sprd_img.encoded_tx_b;
+ spi_size = sprd_img.encoded_tx_size;
+
+ pr_debug("(%d) [Transfer Start, Type = %d, Packet = %d]\n",
+ __LINE__, type, sprd_packet_size);
+ retval = spi_send_modem_bin_execute_cmd
+ (spi_ptr, spi_size, spi_type, crc, &sprd_img);
+ if (retval < 0) {
+ pr_err("(%d) spi_send_modem_bin_execute_cmd fail : %d",
+ __LINE__, retval);
+ goto err0;
+ }
+ M_32_SWAP(img->length);
+
+ /* Send Data */
+ for (i = 0; send_size < img->length; i++) {
+ if (rest_size < sprd_packet_size)
+ data_size = rest_size;
+
+ sprd_img.encoded_tx_size = sprd_packet_size;
+ for (j = 0; j < data_size; j++)
+ *(sprd_img.encoded_tx_b+j) = *(ptr + j);
+
+ spi_type = 0x0002;
+ crc = sprd_crc_calc_fdl
+ ((unsigned short *)sprd_img.encoded_tx_b,
+ sprd_img.encoded_tx_size);
+
+ spi_ptr = sprd_img.encoded_tx_b;
+ spi_size = sprd_img.encoded_tx_size;
+
+ retval = spi_send_modem_bin_execute_cmd
+ (spi_ptr, spi_size, spi_type, crc, &sprd_img);
+ if (retval < 0) {
+ pr_err("(%d) spi_send_modem_bin_execute_cmd fail : %d, %d",
+ __LINE__, retval, i);
+ goto err0;
+ }
+
+ send_size += data_size;
+ rest_size -= data_size;
+ ptr += data_size;
+
+ if (!(i % 100))
+ pr_debug("(%d) [%d] 0x%x size done, rest size: 0x%x\n",
+ __LINE__, i, send_size, rest_size);
+ }
+
+ /* Send Transfer End */
+ memset(sprd_img.encoded_tx_b, 0, SPRD_BLOCK_SIZE * 2);
+ sprd_img.encoded_tx_size = 0;
+
+ spi_type = 0x0003;
+ crc = 0;
+
+ spi_ptr = sprd_img.encoded_tx_b;
+ spi_size = sprd_img.encoded_tx_size;
+
+ pr_debug("(%d) [Transfer END]\n", __LINE__);
+ retval = spi_send_modem_bin_execute_cmd
+ (spi_ptr, spi_size, spi_type, crc, &sprd_img);
+ if (retval < 0) {
+ pr_err("(%d) spi_send_modem_bin_execute_cmd fail : %d",
+ __LINE__, retval);
+ goto err0;
+ }
+
+err0:
+ kfree(sprd_img.decoded_rx_b);
+err1:
+ kfree(sprd_img.encoded_tx_b);
+err2:
+ kfree(sprd_img.rx_b);
+err3:
+ kfree(sprd_img.tx_b);
+
+ return retval;
+
+}
+
+static void spi_send_modem_bin(struct work_struct *send_modem_w)
+{
+ int retval;
+ struct image_buf img;
+ unsigned long tick1, tick2 = 0;
+
+ tick1 = jiffies_to_msecs(jiffies);
+
+ retval = spi_send_modem_bin_xmit_img(MODEM_MAIN, &img);
+ if (retval < 0) {
+ pr_err("(%d) spi_send_modem_bin_xmit_img fail : %d",
+ __LINE__, retval);
+ goto err;
+ }
+
+ retval = spi_send_modem_bin_xmit_img(MODEM_DSP, &img);
+ if (retval < 0) {
+ pr_err("(%d) spi_send_modem_bin_xmit_img fail : %d",
+ __LINE__, retval);
+ goto err;
+ }
+
+ retval = spi_send_modem_bin_xmit_img(MODEM_NV, &img);
+ if (retval < 0) {
+ pr_err("(%d) spi_send_modem_bin_xmit_img fail : %d",
+ __LINE__, retval);
+ goto err;
+ }
+
+ retval = spi_send_modem_bin_xmit_img(MODEM_EFS, &img);
+ if (retval < 0) {
+ pr_err("(%d) spi_send_modem_bin_xmit_img fail : %d",
+ __LINE__, retval);
+ goto err;
+ }
+
+ retval = spi_send_modem_bin_xmit_img(MODEM_RUN, &img);
+ if (retval < 0) {
+ pr_err("(%d) spi_send_modem_bin_xmit_img fail : %d",
+ __LINE__, retval);
+ goto err;
+ }
+
+ p_spild->send_modem_spi = 0;
+ p_spild->is_cp_reset = 0;
+ tick2 = jiffies_to_msecs(jiffies);
+ pr_info("Downloading takes %lu msec\n", (tick2-tick1));
+
+ complete_all(&p_spild->ril_init);
+ sprd_boot_done = 1;
+ p_spild->ril_send_cnt = 0;
+
+err:
+ return;
+
+}
+
+static inline int _request_mem(struct ipc_spi *od)
+{
+ if (!p_spild->p_virtual_buff) {
+ pr_err("what\n");
+ od->mmio = vmalloc(od->size);
+ if (!od->mmio) {
+ pr_err("(%d) Failed to vmalloc size : %lu\n", __LINE__,
+ od->size);
+
+ return -EBUSY;
+ } else {
+ pr_err("(%d) vmalloc Done. mmio : 0x%08x\n", __LINE__,
+ (u32)od->mmio);
+ }
+ }
+
+ memset((void *)od->mmio, 0, od->size);
+
+ p_spild->p_virtual_buff = od->mmio;
+
+ return 0;
+}
+
+void spi_tx_timer_callback(unsigned long param)
+{
+ if (p_spild->spi_state == SPI_STATE_TX_WAIT) {
+ p_spild->spi_timer_tx_state = SPI_STATE_TIME_OVER;
+ pr_err("[SPI] spi_tx_timer_callback -timer expires\n");
+ }
+}
+
+void spi_rx_timer_callback(unsigned long param)
+{
+ if (p_spild->spi_state == SPI_STATE_RX_WAIT) {
+ p_spild->spi_timer_rx_state = SPI_STATE_TIME_OVER;
+ pr_err("[SPI] spi_rx_timer_callback -timer expires\n");
+ }
+}
+
+int spi_sema_init(void)
+{
+ pr_info("[SPI] Srdy sema init\n");
+ sema_init(&p_spild->srdy_sem, 0);
+ p_spild->send_modem_spi = 1;
+ return 0;
+}
+
+static void spi_work(struct work_struct *work)
+{
+ int signal_code;
+
+ struct spi_work_type *spi_wq =
+ container_of(work, struct spi_work_type, work);
+
+ signal_code = spi_wq->signal_code;
+
+ if (p_spild->spi_state == SPI_STATE_END
+ || p_spild->spi_state == SPI_STATE_START) {
+ kfree(spi_wq);
+ return;
+ }
+
+ switch (signal_code) {
+ case SPI_WORK_SEND:
+ if (p_spild->spi_state == SPI_STATE_IDLE)
+ spi_tx_work();
+ break;
+ case SPI_WORK_RECEIVE:
+ if (p_spild->spi_state == SPI_STATE_IDLE
+ || p_spild->spi_state == SPI_STATE_TX_TERMINATE
+ || p_spild->spi_state == SPI_STATE_RX_TERMINATE)
+ spi_rx_work();
+ break;
+
+ default:
+ pr_err("[SPI] ERROR(No signal_code in spi_work[%d])\n",
+ signal_code);
+ break;
+ }
+
+ kfree(spi_wq);
+ if (wake_lock_active(&p_spild->spi_wake_lock)) {
+ wake_unlock(&p_spild->spi_wake_lock);
+ pr_err("[SPI] [%s](%d) spi_wakelock unlocked .\n",
+ __func__, __LINE__);
+ }
+}
+
+static int spi_init_ipc(struct spi_link_device *spild)
+{
+ struct link_device *ld = &spild->ld;
+
+ int i;
+
+ /* Make aliases to each IO device */
+ for (i = 0; i < MAX_DEV_FORMAT; i++)
+ spild->iod[i] = link_get_iod_with_format(ld, i);
+
+ spild->iod[IPC_RAW] = spild->iod[IPC_MULTI_RAW];
+
+ /* List up the IO devices connected to each IPC channel */
+ for (i = 0; i < MAX_DEV_FORMAT; i++) {
+ if (spild->iod[i])
+ pr_err("[LNK] <%s:%s> spild->iod[%d]->name = %s\n",
+ __func__, ld->name, i, spild->iod[i]->name);
+ else
+ pr_err("[LNK] <%s:%s> No spild->iod[%d]\n",
+ __func__, ld->name, i);
+ }
+
+ ld->mode = LINK_MODE_IPC;
+
+ return 0;
+}
+
+static int spi_thread(void *data)
+{
+ struct spi_link_device *spild = (struct spi_link_device *)data;
+
+ if (lpcharge == 1) {
+ pr_err("[LPM MODE] spi_thread_exit!\n");
+ return 0;
+ }
+
+ daemonize("spi_thread");
+
+ pr_info("[%s] spi_thread start.\n", __func__);
+ p_spild->boot_done = 1;
+
+ wait_for_completion(&p_spild->ril_init);
+
+ pr_info("[%s] ril_init completed.\n", __func__);
+
+ pr_info("<%s> wait 2 sec... srdy : %d\n",
+ __func__, gpio_get_value(spild->gpio_ipc_srdy));
+ msleep_interruptible(1700);
+
+ while (gpio_get_value(spild->gpio_ipc_srdy))
+ ;
+ pr_info("(%s) cp booting... Done.\n", __func__);
+
+ spi_init_ipc(spild);
+
+ pr_info("[spi_thread] Start IPC Communication. SRDY : %d\n",
+ gpio_get_value(spild->gpio_ipc_srdy));
+
+ /* CP booting is already completed, just set submrdy to high */
+ if (gpio_get_value(spild->gpio_ipc_sub_srdy) == SPI_GPIOLEVEL_HIGH) {
+ gpio_set_value(spild->gpio_ipc_sub_mrdy, SPI_GPIOLEVEL_HIGH);
+ pr_err("[spi_thread] CP booting is already completed\n");
+ }
+ /* CP booting is not completed.
+ set submrdy to high and wait until subsrdy is high */
+ else {
+ pr_err("[spi_thread] CP booting is not completed. wait...\n");
+
+ gpio_set_value(spild->gpio_ipc_sub_mrdy, SPI_GPIOLEVEL_HIGH);
+ do {
+ msleep_interruptible(5);
+ } while (gpio_get_value(spild->gpio_ipc_sub_srdy) ==
+ SPI_GPIOLEVEL_LOW);
+
+ pr_err("[spi_thread] CP booting is done...\n");
+ }
+
+ if (p_spild->spi_is_restart)
+ msleep_interruptible(100);
+ else
+ msleep_interruptible(30);
+
+ gpio_set_value(spild->gpio_ipc_sub_mrdy, SPI_GPIOLEVEL_LOW);
+
+ pr_info("(%s) spi sync done.\n", __func__);
+
+ spild->spi_state = SPI_STATE_IDLE;
+ p_spild->spi_is_restart = 0;
+
+ return 0;
+}
+
+static int spi_probe(struct spi_device *spi)
+{
+ int ret;
+
+ p_spi = spi;
+ p_spi->mode = SPI_MODE_1;
+ p_spi->bits_per_word = 32;
+
+ ret = spi_setup(p_spi);
+ if (ret != 0) {
+
+ pr_err("[%s] spi setup error\n", __func__);
+
+ return ret;
+ }
+
+ pr_info("[%s] spi probe done\n", __func__);
+
+ return ret;
+}
+
+static int spi_remove(struct spi_device *spi)
+{
+ return 0;
+}
+
+static struct spi_driver spi_driver = {
+ .probe = spi_probe,
+ .remove = __devexit_p(spi_remove),
+ .driver = {
+ .name = "modem_if_spi",
+ .bus = &spi_bus_type,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int spi_link_init(void)
+{
+ int ret;
+ struct ipc_spi *od;
+ struct task_struct *th;
+ struct link_device *ld = &p_spild->ld;
+
+ p_spild->gpio_modem_bin_srdy = p_spild->gpio_ipc_srdy;
+
+ pr_info("(%d) gpio_mrdy : %d, gpio_srdy : %d(%d)\n", __LINE__,
+ p_spild->gpio_ipc_mrdy, p_spild->gpio_modem_bin_srdy,
+ gpio_get_value(p_spild->gpio_ipc_srdy));
+
+ od = kzalloc(sizeof(struct ipc_spi), GFP_KERNEL);
+ if (!od) {
+ pr_err("(%d) failed to allocate device\n", __LINE__);
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ od->base = 0;
+ od->size = SZ_16M; /* 16M */
+ if (p_spild->p_virtual_buff)
+ od->mmio = p_spild->p_virtual_buff;
+ ret = _request_mem(od);
+ if (ret)
+ goto err;
+
+ init_completion(&p_spild->ril_init);
+ sema_init(&p_spild->srdy_sem, 0);
+
+ INIT_WORK(&p_spild->send_modem_w,
+ spi_send_modem_bin);
+
+ wake_lock_init(&p_spild->spi_wake_lock,
+ WAKE_LOCK_SUSPEND,
+ "samsung-spiwakelock");
+
+ /* Register SPI Srdy interrupt handler */
+ ret = spi_register_isr(gpio_to_irq(p_spild->gpio_ipc_srdy),
+ spi_srdy_irq_handler, IRQF_TRIGGER_RISING,
+ "spi_srdy_rising", ld);
+
+ if (ret)
+ goto err;
+
+ /* Register SPI SubSrdy interrupt handler */
+ ret = spi_register_isr(gpio_to_irq(p_spild->gpio_ipc_sub_srdy),
+ spi_subsrdy_irq_handler, IRQF_TRIGGER_RISING,
+ "spi_subsrdy_rising", ld);
+
+ if (ret)
+ goto err;
+
+ th = kthread_create(spi_thread, (void *)p_spild, "spi_thread");
+ if (IS_ERR(th)) {
+ pr_err("kernel_thread() failed\n");
+ goto err;
+ }
+ wake_up_process(th);
+
+ pr_info("[%s] Done\n", __func__);
+ return 0;
+
+err:
+ kfree(od);
+ return ret;
+}
+
+/*=====================================
+* Description spi restart for CP slient reset
+=====================================*/
+void spi_set_restart(void)
+{
+ struct link_device *ld = &p_spild->ld;
+
+ pr_info("[SPI] spi_set_restart(spi_main_state=[%d])\n",
+ (int)p_spild->spi_state);
+
+ gpio_set_value(p_spild->gpio_ipc_sub_mrdy, SPI_GPIOLEVEL_LOW);
+
+ p_spild->spi_state = SPI_STATE_END;
+ p_spild->spi_is_restart = 1;
+
+ /* Flush SPI work queue */
+ flush_workqueue(p_spild->spi_wq);
+
+ /* Unregister SPI Srdy interrupt handler */
+ spi_unregister_isr(gpio_to_irq(p_spild->gpio_ipc_srdy), ld);
+
+ /* Unregister SPI SubSrdy interrupt handler */
+ spi_unregister_isr(gpio_to_irq(p_spild->gpio_ipc_sub_srdy), ld);
+
+ if (wake_lock_active(&p_spild->spi_wake_lock)) {
+ wake_unlock(&p_spild->spi_wake_lock);
+ pr_err("[SPI] [%s](%d) spi_wakelock unlocked.\n",
+ __func__, __LINE__);
+ }
+ wake_lock_destroy(&p_spild->spi_wake_lock);
+}
+
+
+int spi_thread_restart(void)
+{
+ int retval;
+
+ p_spild->send_modem_spi = 1;
+ p_spild->is_cp_reset = 1;
+ sprd_boot_done = 0;
+
+ pr_info("[modem_if_spi] spi_thread_restart\n");
+
+ spi_set_restart();
+
+ /* Create SPI device */
+ retval = spi_link_init();
+ if (retval)
+ goto exit;
+
+ return 0;
+
+exit:
+ return retval;
+}
+EXPORT_SYMBOL(spi_thread_restart);
+
+struct link_device *spi_create_link_device(struct platform_device *pdev)
+{
+ struct spi_link_device *spild = NULL;
+ struct link_device *ld;
+ struct modem_data *pdata;
+
+ int ret;
+ int i;
+
+ /* Get the platform data */
+ pdata = (struct modem_data *)pdev->dev.platform_data;
+ if (!pdata) {
+ pr_err("[LNK/E] <%s> pdata == NULL\n", __func__);
+ goto err;
+ }
+
+ pr_info("[LNK] <%s> link device = %s\n", __func__, pdata->link_name);
+ pr_info("[LNK] <%s> modem = %s\n", __func__, pdata->name);
+
+ /* Alloc SPI link device structure */
+ p_spild = spild = kzalloc(sizeof(struct spi_link_device), GFP_KERNEL);
+ if (!spild) {
+ pr_err("[LNK/E] <%s> Failed to kzalloc()\n", __func__);
+ goto err;
+ }
+ ld = &spild->ld;
+
+ /* Extract modem data and SPI control data from the platform data */
+ ld->mdm_data = pdata;
+ ld->name = "spi";
+
+ if (ld->aligned)
+ pr_err("[LNK] <%s> Aligned access is required!!!\n", __func__);
+
+
+ ld->send = spi_send;
+
+ INIT_LIST_HEAD(&ld->list);
+
+ skb_queue_head_init(&ld->sk_fmt_tx_q);
+ skb_queue_head_init(&ld->sk_raw_tx_q);
+ skb_queue_head_init(&ld->sk_rfs_tx_q);
+ ld->skb_txq[IPC_FMT] = &ld->sk_fmt_tx_q;
+ ld->skb_txq[IPC_RAW] = &ld->sk_raw_tx_q;
+ ld->skb_txq[IPC_RFS] = &ld->sk_rfs_tx_q;
+
+ spild->spi_wq = create_singlethread_workqueue("spi_wq");
+ if (!spild->spi_wq) {
+ pr_err("[LNK/E] <%s> Fail to create workqueue for spi_wq\n",
+ __func__);
+ goto err;
+ }
+
+ spild->spi_state = SPI_STATE_END;
+ spild->max_ipc_dev = IPC_RFS+1; /* FMT, RAW, RFS */
+
+ for (i = 0; i < spild->max_ipc_dev; i++)
+ skb_queue_head_init(&spild->skb_rxq[i]);
+
+ /* Prepare a clean buffer for SPI access */
+ spild->buff = kzalloc(SPI_MAX_PACKET_SIZE, GFP_KERNEL);
+ spild->sync_buff = kzalloc(SPI_MAX_PACKET_SIZE, GFP_KERNEL);
+
+ memset(spild->buff , 0, SPI_MAX_PACKET_SIZE);
+ memset(spild->sync_buff , 0, SPI_MAX_PACKET_SIZE);
+
+ if (!spild->buff) {
+ pr_err("[LNK/E] <%s> Failed to alloc spild->buff\n", __func__);
+ goto err;
+ }
+
+ /* Initialize SPI pin value */
+ spild->gpio_ipc_mrdy = pdata->gpio_ipc_mrdy;
+ spild->gpio_ipc_srdy = pdata->gpio_ipc_srdy;
+ spild->gpio_ipc_sub_mrdy = pdata->gpio_ipc_sub_mrdy;
+ spild->gpio_ipc_sub_srdy = pdata->gpio_ipc_sub_srdy;
+
+ spild->gpio_ap_cp_int1 = pdata->gpio_ap_cp_int1;
+ spild->gpio_ap_cp_int2 = pdata->gpio_ap_cp_int2;
+
+ /* Create SPI Timer */
+ init_timer(&spild->spi_tx_timer);
+
+ spild->spi_tx_timer.expires =
+ jiffies + ((SPI_TIMER_TX_WAIT_TIME * HZ) / 1000);
+
+ spild->spi_tx_timer.data = 0;
+ spild->spi_tx_timer.function = spi_tx_timer_callback;
+
+ init_timer(&spild->spi_rx_timer);
+
+ spild->spi_rx_timer.expires =
+ jiffies + ((SPI_TIMER_RX_WAIT_TIME * HZ) / 1000);
+
+ spild->spi_rx_timer.data = 0;
+ spild->spi_rx_timer.function = spi_rx_timer_callback;
+
+ ret = spi_register_driver(&spi_driver);
+ if (ret < 0) {
+ pr_err("spi_register_driver() fail : %d\n", ret);
+ goto err;
+ }
+
+ /* Create SPI device */
+ ret = spi_link_init();
+ if (ret)
+ goto err;
+
+ /* creat work queue thread */
+ p_spild->ipc_spi_wq = create_singlethread_workqueue("ipc_spi_wq");
+
+ if (!p_spild->ipc_spi_wq) {
+ pr_err("[%s] get workqueue thread fail\n",
+ __func__);
+
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ return ld;
+
+err:
+ kfree(spild->buff);
+ kfree(spild);
+ return NULL;
+}
diff --git a/drivers/misc/modem_if/modem_link_device_spi.h b/drivers/misc/modem_if/modem_link_device_spi.h
new file mode 100644
index 0000000..2289a46
--- /dev/null
+++ b/drivers/misc/modem_if/modem_link_device_spi.h
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) 2011 Google, Inc.
+ * Copyright (C) 2010 Samsung Electronics.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef __MODEM_LINK_DEVICE_SPI_H__
+#define __MODEM_LINK_DEVICE_SPI_H__
+
+#include <linux/wakelock.h>
+#include <linux/workqueue.h>
+#include <linux/timer.h>
+#include <linux/platform_data/modem.h>
+
+
+#define SPI_TIMER_TX_WAIT_TIME 60 /* ms */
+#define SPI_TIMER_RX_WAIT_TIME 500 /* ms */
+
+#define SPI_MAX_PACKET_SIZE (2048 * 6)
+#define SPI_TASK_EVENT_COUNT 64
+#define SPI_DATA_BOF 0x7F
+#define SPI_DATA_EOF 0x7E
+#define SPI_DATA_FF_PADDING_HEADER 0xFFFFFFFF
+
+#define SPI_DATA_MUX_NORMAL_MASK 0x0F
+#define SPI_DATA_MUX_MORE_H 0x10
+#define SPI_DATA_MUX_MORE_M 0x20
+#define SPI_DATA_MUX_MORE_T 0x30
+
+#define SPI_DATA_MUX_SIZE 2
+#define SPI_DATA_LENGTH_SIZE 4
+#define SPI_DATA_BOF_SIZE 1
+#define SPI_DATA_INNER_LENGTH_SIZE 4
+#define SPI_DATA_IPC_INNER_LENGTH_SIZE 2
+#define SPI_DATA_IPC_INNER_CONTROL_SIZE 1
+#define SPI_DATA_EOF_SIZE 1
+#define SPI_DATA_HEADER_SIZE (SPI_DATA_MUX_SIZE+ \
+ SPI_DATA_LENGTH_SIZE+SPI_DATA_BOF_SIZE+SPI_DATA_EOF_SIZE)
+#define SPI_DATA_HEADER_SIZE_FRONT (SPI_DATA_MUX_SIZE+ \
+ SPI_DATA_LENGTH_SIZE+SPI_DATA_BOF_SIZE)
+#define SPI_DATA_IPC_INNER_HEADER_SIZE \
+ (SPI_DATA_IPC_INNER_LENGTH_SIZE+ \
+ SPI_DATA_IPC_INNER_CONTROL_SIZE)
+#define SPI_DATA_SIZE(X) (SPI_DATA_HEADER_SIZE+X)
+
+#define SPI_DATA_LENGTH_OFFSET SPI_DATA_MUX_SIZE
+#define SPI_DATA_BOF_OFFSET (SPI_DATA_LENGTH_OFFSET+ \
+ SPI_DATA_LENGTH_SIZE)
+#define SPI_DATA_DATA_OFFSET (SPI_DATA_BOF_OFFSET+ \
+ SPI_DATA_BOF_SIZE)
+#define SPI_DATA_EOF_OFFSET(X) (SPI_DATA_DATA_OFFSET+X)
+
+#define SPI_DATA_PACKET_HEADER_SIZE 4
+#define SPI_DATA_PACKET_MUX_ERROR_SPARE_SIZE 4
+#define SPI_DATA_PACKET_MAX_PACKET_BODY_SIZE \
+ (SPI_MAX_PACKET_SIZE-SPI_DATA_PACKET_HEADER_SIZE- \
+ SPI_DATA_PACKET_MUX_ERROR_SPARE_SIZE)
+
+#define SPI_DATA_MIN_SIZE (SPI_DATA_HEADER_SIZE*2)
+#define SPI_DATA_MAX_SIZE_PER_PACKET (SPI_MAX_PACKET_SIZE- \
+ SPI_DATA_PACKET_HEADER_SIZE-SPI_DATA_HEADER_SIZE- \
+ SPI_DATA_PACKET_MUX_ERROR_SPARE_SIZE)
+
+#define SPI_DATA_DIVIDE_BUFFER_SIZE SPI_MAX_PACKET_SIZE
+
+struct spi_work_type {
+ struct work_struct work;
+ int signal_code;
+};
+
+enum spi_msg_t {
+ SPI_WORK_SEND,
+ SPI_WORK_RECEIVE
+};
+
+enum spi_state_t {
+ SPI_STATE_START, /* before init complete */
+ SPI_STATE_INIT, /* initialising */
+ SPI_STATE_IDLE, /* suspend. Waiting for event */
+ /* state to start tx. Become from idle */
+ SPI_STATE_TX_START,
+ SPI_STATE_TX_CONFIRM,
+ /* (in case of master) */
+ /* wait srdy rising interrupt to check slave preparing */
+ /* (in case of slave) */
+ /* wait submrdy rising interrupt to check sync complete */
+ SPI_STATE_TX_WAIT,
+ SPI_STATE_TX_SENDING, /* tx data sending */
+ SPI_STATE_TX_TERMINATE,
+ SPI_STATE_TX_MORE,
+
+ /* in case of slave, wait submrdy rising interrupt to */
+ SPI_STATE_RX_WAIT,
+
+ /* check sync complete then it starts to read buffer */
+ SPI_STATE_RX_MORE,
+ SPI_STATE_RX_TERMINATE,
+ SPI_STATE_END /* spi task is stopped */
+};
+
+enum spi_timer_state_t {
+ SPI_STATE_TIME_START,
+ SPI_STATE_TIME_OVER
+};
+
+enum spi_gpiolevel_t {
+ SPI_GPIOLEVEL_LOW = 0,
+ SPI_GPIOLEVEL_HIGH
+};
+
+enum spi_data_type_t {
+ SPI_DATA_MUX_IPC = 0x01,
+ SPI_DATA_MUX_RAW = 0x02,
+ SPI_DATA_MUX_RFS = 0x03,
+ SPI_DATA_MUX_CMD = 0x04,
+};
+
+struct spi_data_packet_header {
+ /* 12bit : packet size less than SPI_DEV_PACKET_SIZE */
+ unsigned long current_data_size:31;
+ /* 1bit : packet division flag */
+ unsigned long more:1;
+};
+
+struct spi_link_device {
+ struct link_device ld;
+
+ /* Link to SPI control functions dependent on each platform */
+ int max_ipc_dev;
+
+ /* Wakelock for SPI device */
+ struct wake_lock spi_wake_lock;
+ /* Workqueue for modem bin transfers */
+ struct workqueue_struct *ipc_spi_wq;
+
+ /* SPI state */
+ int spi_state;
+ int spi_is_restart;
+
+ /* SPI Timer state */
+ int spi_timer_tx_state;
+ int spi_timer_rx_state;
+
+ /* SPI GPIO pins */
+ unsigned int gpio_ipc_mrdy;
+ unsigned int gpio_ipc_srdy;
+ unsigned int gpio_ipc_sub_mrdy;
+ unsigned int gpio_ipc_sub_srdy;
+
+ unsigned int gpio_modem_bin_srdy;
+
+ unsigned int gpio_ap_cp_int1;
+ unsigned int gpio_ap_cp_int2;
+
+ /* value for checking spi pin status */
+ unsigned long mrdy_low_time_save;
+ unsigned long mrdy_high_time_save;
+
+ /* SPI Wait Timer */
+ struct timer_list spi_tx_timer;
+ struct timer_list spi_rx_timer;
+
+ struct workqueue_struct *spi_wq;
+ struct spi_work_type spi_work;
+
+ /* For send modem bin */
+ struct work_struct send_modem_w;
+
+ struct io_device *iod[MAX_DEV_FORMAT];
+ struct sk_buff_head skb_rxq[MAX_DEV_FORMAT];
+
+ /* Multi-purpose miscellaneous buffer */
+ u8 *buff;
+ u8 *sync_buff;
+
+ struct completion ril_init;
+ struct semaphore srdy_sem;
+
+ int send_modem_spi;
+ int is_cp_reset;
+ int boot_done;
+ int ril_send_modem_img;
+ unsigned long ril_send_cnt;
+
+ void __iomem *p_virtual_buff;
+};
+
+/* converts from struct link_device* to struct xxx_link_device* */
+#define to_spi_link_device(linkdev) \
+ container_of(linkdev, struct spi_link_device, ld)
+
+extern unsigned int lpcharge;
+extern int get_console_suspended(void);
+static void spi_work(struct work_struct *work);
+
+/* Send SPRD main image through SPI */
+#define SPRD_BLOCK_SIZE 32768
+
+enum image_type {
+ MODEM_MAIN,
+ MODEM_DSP,
+ MODEM_NV,
+ MODEM_EFS,
+ MODEM_RUN,
+};
+
+struct image_buf {
+ unsigned int length;
+ unsigned int offset;
+ unsigned int address;
+ unsigned char *buf;
+};
+
+struct sprd_image_buf {
+ u8 *tx_b;
+ u8 *rx_b;
+ u8 *encoded_tx_b;
+ u8 *decoded_rx_b;
+
+ int tx_size;
+ int rx_size;
+ int encoded_tx_size;
+ int decoded_rx_size;
+};
+
+/* CRC */
+#define CRC_16_L_OK 0x0
+#define HDLC_FLAG 0x7E
+#define HDLC_ESCAPE 0x7D
+#define HDLC_ESCAPE_MASK 0x20
+#define CRC_CHECK_SIZE 0x02
+
+#define M_32_SWAP(a) { \
+ u32 _tmp; \
+ _tmp = a; \
+ ((u8 *)&a)[0] = ((u8 *)&_tmp)[3]; \
+ ((u8 *)&a)[1] = ((u8 *)&_tmp)[2]; \
+ ((u8 *)&a)[2] = ((u8 *)&_tmp)[1]; \
+ ((u8 *)&a)[3] = ((u8 *)&_tmp)[0]; \
+ }
+
+#define M_16_SWAP(a) { \
+ u16 _tmp; \
+ _tmp = (u16)a; \
+ ((u8 *)&a)[0] = ((u8 *)&_tmp)[1]; \
+ ((u8 *)&a)[1] = ((u8 *)&_tmp)[0]; \
+ }
+
+#endif
diff --git a/drivers/misc/modem_if/modem_link_device_usb.c b/drivers/misc/modem_if/modem_link_device_usb.c
index 615971e..5b7c98b 100644
--- a/drivers/misc/modem_if/modem_link_device_usb.c
+++ b/drivers/misc/modem_if/modem_link_device_usb.c
@@ -32,6 +32,8 @@
#include "modem_utils.h"
#include "modem_link_pm_usb.h"
+#include <mach/regs-gpio.h>
+
#define URB_COUNT 4
static int usb_tx_urb_with_skb(struct usb_link_device *usb_ld,
@@ -61,8 +63,8 @@ static int start_ipc(struct link_device *ld, struct io_device *iod)
struct usb_link_device *usb_ld = to_usb_link_device(ld);
struct if_usb_devdata *pipe_data = &usb_ld->devdata[IF_USB_FMT_EP];
- if (usb_ld->link_pm_data->hub_handshake_done) {
- mif_err("Aleady send start ipc, skip start ipc\n");
+ if (has_hub(usb_ld) && usb_ld->link_pm_data->hub_handshake_done) {
+ mif_err("Already send start ipc, skip start ipc\n");
err = 0;
goto exit;
}
@@ -73,8 +75,9 @@ static int start_ipc(struct link_device *ld, struct io_device *iod)
goto exit;
}
- if (usb_ld->if_usb_initstates == INIT_IPC_START_DONE) {
- mif_debug("aleady IPC started\n");
+ if (has_hub(usb_ld) &&
+ usb_ld->if_usb_initstates == INIT_IPC_START_DONE) {
+ mif_debug("Already IPC started\n");
err = 0;
goto exit;
}
@@ -262,8 +265,8 @@ static void usb_tx_complete(struct urb *urb)
usb_mark_last_busy(urb->dev);
ret = pm_runtime_put_autosuspend(&urb->dev->dev);
- if (ret < 0)
- mif_debug("pm_runtime_put_autosuspend failed : ret(%d)\n", ret);
+ if (ret < 0 && ret != -EAGAIN)
+ mif_debug("pm_runtime_put_autosuspend failed: %d\n", ret);
usb_free_urb(urb);
dev_kfree_skb_any(skb);
}
@@ -274,9 +277,18 @@ static void if_usb_force_disconnect(struct work_struct *work)
container_of(work, struct usb_link_device, disconnect_work);
struct usb_device *udev = usb_ld->usbdev;
+ /* if already disconnected before run this workqueue */
+ if (!udev || !(&udev->dev) || !usb_ld->if_usb_connected)
+ return;
+
+ /* disconnect udev's parent if usb hub used */
+ if (has_hub(usb_ld))
+ udev = udev->parent;
+
pm_runtime_get_sync(&udev->dev);
if (udev->state != USB_STATE_NOTATTACHED) {
- mif_info("force disconnect by modem not responding!!\n");
+ usb_force_disconnect(udev);
+ mif_info("force disconnect\n");
}
pm_runtime_put_autosuspend(&udev->dev);
}
@@ -438,6 +450,7 @@ static void usb_tx_work(struct work_struct *work)
static int if_usb_suspend(struct usb_interface *intf, pm_message_t message)
{
struct usb_link_device *usb_ld = usb_get_intfdata(intf);
+ struct link_pm_data *pm_data = usb_ld->link_pm_data;
int i;
if (atomic_inc_return(&usb_ld->suspend_count) == IF_USB_DEVNUM_MAX) {
@@ -446,8 +459,8 @@ static int if_usb_suspend(struct usb_interface *intf, pm_message_t message)
for (i = 0; i < IF_USB_DEVNUM_MAX; i++)
usb_kill_anchored_urbs(&usb_ld->devdata[i].reading);
- if (usb_ld->link_pm_data->cpufreq_unlock)
- usb_ld->link_pm_data->cpufreq_unlock();
+ if (pm_data->freq_unlock)
+ pm_data->freq_unlock(&usb_ld->usbdev->dev);
wake_unlock(&usb_ld->susplock);
}
@@ -472,10 +485,16 @@ static void post_resume_work(struct work_struct *work)
{
struct usb_link_device *usb_ld = container_of(work,
struct usb_link_device, post_resume_work.work);
+ struct link_pm_data *pm_data = usb_ld->link_pm_data;
+ struct usb_device *udev = usb_ld->usbdev;
- /* lock cpu frequency when L2->L0 */
- if (usb_ld->link_pm_data->cpufreq_lock)
- usb_ld->link_pm_data->cpufreq_lock();
+ /* if already disconnected before run this workqueue */
+ if (!udev || !(&udev->dev) || !usb_ld->if_usb_connected)
+ return;
+
+ /* lock cpu/bus frequency when L2->L0 */
+ if (pm_data->freq_lock)
+ pm_data->freq_lock(&udev->dev);
}
static void wait_enumeration_work(struct work_struct *work)
@@ -532,10 +551,11 @@ static int if_usb_resume(struct usb_interface *intf)
skb = urb->context;
dev_kfree_skb_any(skb);
usb_free_urb(urb);
- if (pm_runtime_put_autosuspend(
- &usb_ld->usbdev->dev) < 0)
- mif_debug(
- "pm_runtime_put_autosuspend fail\n");
+ ret = pm_runtime_put_autosuspend(
+ &usb_ld->usbdev->dev);
+ if (ret < 0 && ret != -EAGAIN)
+ mif_debug("pm_runtime_put_autosuspend "
+ "failed: %d\n", ret);
}
}
SET_SLAVE_WAKEUP(usb_ld->pdata, 1);
@@ -576,9 +596,11 @@ static void if_usb_disconnect(struct usb_interface *intf)
{
struct usb_link_device *usb_ld = usb_get_intfdata(intf);
struct usb_device *usbdev = usb_ld->usbdev;
+ struct link_pm_data *pm_data = usb_ld->link_pm_data;
int dev_id = intf->altsetting->desc.bInterfaceNumber;
struct if_usb_devdata *pipe_data = &usb_ld->devdata[dev_id];
+
usb_set_intfdata(intf, NULL);
pipe_data->disconnected = 1;
@@ -602,6 +624,7 @@ static void if_usb_disconnect(struct usb_interface *intf)
cancel_delayed_work_sync(&usb_ld->ld.tx_delayed_work);
usb_put_dev(usbdev);
usb_ld->usbdev = NULL;
+ pm_runtime_forbid(pm_data->root_hub);
}
}
@@ -642,7 +665,6 @@ static int __devinit if_usb_probe(struct usb_interface *intf,
dev_id, id, usb_ld);
usb_ld->usbdev = usbdev;
-
usb_get_dev(usbdev);
for (i = 0; i < IF_USB_DEVNUM_MAX; i++) {
@@ -726,7 +748,8 @@ static int __devinit if_usb_probe(struct usb_interface *intf,
usb_ld->host_wake_timeout_flag = 0;
if (gpio_get_value(usb_ld->pdata->gpio_phone_active)) {
- int delay = usb_ld->link_pm_data->autosuspend_delay_ms ?:
+ struct link_pm_data *pm_data = usb_ld->link_pm_data;
+ int delay = pm_data->autosuspend_delay_ms ?:
DEFAULT_AUTOSUSPEND_DELAY_MS;
pm_runtime_set_autosuspend_delay(&usbdev->dev, delay);
dev = &usbdev->dev;
@@ -741,13 +764,13 @@ static int __devinit if_usb_probe(struct usb_interface *intf,
dev_name(ehci_dev));
pm_runtime_allow(ehci_dev);
- if (has_hub(usb_ld)) {
- usb_ld->link_pm_data->hub_status =
- (usb_ld->link_pm_data->root_hub) ?
- HUB_STATE_PREACTIVE : HUB_STATE_ACTIVE;
- }
+ if (!pm_data->autosuspend)
+ pm_runtime_forbid(dev);
- usb_ld->link_pm_data->root_hub = root_hub;
+ if (has_hub(usb_ld))
+ link_pm_preactive(pm_data);
+
+ pm_data->root_hub = root_hub;
}
usb_ld->flow_suspend = 0;
@@ -764,6 +787,14 @@ static int __devinit if_usb_probe(struct usb_interface *intf,
usb_change_modem_state(usb_ld, STATE_LOADER_DONE);
}
+ /* check dynamic switching gpio received
+ * before usb enumeration is completed
+ */
+ if (ld->mc->need_switch_to_usb) {
+ ld->mc->need_switch_to_usb = false;
+ rawdevs_set_tx_link(ld->msd, LINKDEV_USB);
+ }
+
return 0;
out2:
@@ -791,6 +822,13 @@ irqreturn_t usb_resume_irq(int irq, void *data)
wake_status = hwup;
irq_set_irq_type(irq, hwup ? IRQF_TRIGGER_LOW : IRQF_TRIGGER_HIGH);
+ /*
+ * exynos BSP has problem when using level interrupt.
+ * If we change irq type from interrupt handler,
+ * we can get level interrupt twice.
+ * this is temporary solution until SYS.LSI resolve this problem.
+ */
+ __raw_writel(eint_irq_to_bit(irq), S5P_EINT_PEND(EINT_REG_NR(irq)));
wake_lock_timeout(&usb_ld->gpiolock, 100);
mif_err("< H-WUP %d\n", hwup);
@@ -945,6 +983,15 @@ static void __exit if_usb_exit(void)
usb_deregister(&if_usb_driver);
}
+bool usb_is_enumerated(struct modem_shared *msd)
+{
+ struct link_device *ld = find_linkdev(msd, LINKDEV_USB);
+ if (ld)
+ return to_usb_link_device(ld)->usbdev != NULL;
+ else
+ return false;
+}
+
/* lte specific functions */
diff --git a/drivers/misc/modem_if/modem_link_device_usb.h b/drivers/misc/modem_if/modem_link_device_usb.h
index 44f6b1b..8233fd1 100644
--- a/drivers/misc/modem_if/modem_link_device_usb.h
+++ b/drivers/misc/modem_if/modem_link_device_usb.h
@@ -52,7 +52,6 @@ enum IPC_INIT_STATUS {
enum hub_status {
HUB_STATE_OFF, /* usb3503 0ff*/
HUB_STATE_RESUMMING, /* usb3503 on, but enummerattion was not yet*/
- HUB_STATE_PREACTIVE,
HUB_STATE_ACTIVE, /* hub and CMC221 enumerate */
};
@@ -128,5 +127,6 @@ do { \
#define has_hub(usb_ld) ((usb_ld)->link_pm_data->has_usbhub)
irqreturn_t usb_resume_irq(int irq, void *data);
+bool usb_is_enumerated(struct modem_shared *msd);
#endif
diff --git a/drivers/misc/modem_if/modem_link_pm_usb.c b/drivers/misc/modem_if/modem_link_pm_usb.c
index 244256f..1b2614e 100644
--- a/drivers/misc/modem_if/modem_link_pm_usb.c
+++ b/drivers/misc/modem_if/modem_link_pm_usb.c
@@ -27,15 +27,34 @@
#include "modem_link_pm_usb.h"
+static inline void start_hub_work(struct link_pm_data *pm_data, int delay)
+{
+ if (pm_data->hub_work_running == false) {
+ pm_data->hub_work_running = true;
+ wake_lock(&pm_data->hub_lock);
+ mif_debug("link_pm_hub_work is started\n");
+ }
+
+ schedule_delayed_work(&pm_data->link_pm_hub, msecs_to_jiffies(delay));
+}
+
+static inline void end_hub_work(struct link_pm_data *pm_data)
+{
+ wake_unlock(&pm_data->hub_lock);
+ pm_data->hub_work_running = false;
+ mif_debug("link_pm_hub_work is done\n");
+}
+
bool link_pm_is_connected(struct usb_link_device *usb_ld)
{
if (has_hub(usb_ld)) {
- if (usb_ld->link_pm_data->hub_init_lock)
+ struct link_pm_data *pm_data = usb_ld->link_pm_data;
+ if (pm_data->hub_init_lock)
return false;
- if (usb_ld->link_pm_data->hub_status != HUB_STATE_ACTIVE) {
- schedule_delayed_work(
- &usb_ld->link_pm_data->link_pm_hub, 0);
+ if (pm_data->hub_status == HUB_STATE_OFF) {
+ if (pm_data->hub_work_running == false)
+ start_hub_work(pm_data, 0);
return false;
}
}
@@ -48,27 +67,40 @@ bool link_pm_is_connected(struct usb_link_device *usb_ld)
return true;
}
+void link_pm_preactive(struct link_pm_data *pm_data)
+{
+ if (pm_data->root_hub) {
+ mif_info("pre-active\n");
+ pm_data->hub_on_retry_cnt = 0;
+ complete(&pm_data->hub_active);
+ pm_runtime_put_sync(pm_data->root_hub);
+ }
+
+ pm_data->hub_status = HUB_STATE_ACTIVE;
+}
+
static void link_pm_hub_work(struct work_struct *work)
{
int err;
struct link_pm_data *pm_data =
container_of(work, struct link_pm_data, link_pm_hub.work);
- if (pm_data->hub_status == HUB_STATE_ACTIVE)
+ if (pm_data->hub_status == HUB_STATE_ACTIVE) {
+ end_hub_work(pm_data);
return;
+ }
if (!pm_data->port_enable) {
mif_err("mif: hub power func not assinged\n");
+ end_hub_work(pm_data);
return;
}
- wake_lock(&pm_data->hub_lock);
/* If kernel if suspend, wait the ehci resume */
if (pm_data->dpm_suspending) {
mif_info("dpm_suspending\n");
- schedule_delayed_work(&pm_data->link_pm_hub,
- msecs_to_jiffies(500));
- goto exit;
+ start_hub_work(pm_data, 500);
+ return;
}
switch (pm_data->hub_status) {
@@ -88,11 +120,11 @@ static void link_pm_hub_work(struct work_struct *work)
pm_data->hub_status = HUB_STATE_OFF;
if (pm_data->root_hub)
pm_runtime_put_sync(pm_data->root_hub);
- goto exit;
+ end_hub_work(pm_data);
+ } else {
+ /* resume root hub */
+ start_hub_work(pm_data, 100);
}
- /* resume root hub */
- schedule_delayed_work(&pm_data->link_pm_hub,
- msecs_to_jiffies(100));
break;
case HUB_STATE_RESUMMING:
if (pm_data->hub_on_retry_cnt++ > 50) {
@@ -100,34 +132,26 @@ static void link_pm_hub_work(struct work_struct *work)
pm_data->hub_status = HUB_STATE_OFF;
if (pm_data->root_hub)
pm_runtime_put_sync(pm_data->root_hub);
+ end_hub_work(pm_data);
+ } else {
+ mif_info("hub resumming: %d\n",
+ pm_data->hub_on_retry_cnt);
+ start_hub_work(pm_data, 200);
}
- mif_trace("hub resumming\n");
- schedule_delayed_work(&pm_data->link_pm_hub,
- msecs_to_jiffies(200));
- break;
- case HUB_STATE_PREACTIVE:
- pm_data->hub_status = HUB_STATE_ACTIVE;
- mif_trace("hub active\n");
- pm_data->hub_on_retry_cnt = 0;
- wake_unlock(&pm_data->hub_lock);
- complete(&pm_data->hub_active);
- if (pm_data->root_hub)
- pm_runtime_put_sync(pm_data->root_hub);
break;
}
exit:
return;
}
-static int link_pm_hub_standby(struct link_pm_data *pm_data)
+static int link_pm_hub_standby(void *args)
{
+ struct link_pm_data *pm_data = args;
struct usb_link_device *usb_ld = pm_data->usb_ld;
int err = 0;
- mif_info("wait hub standby\n");
-
if (!pm_data->port_enable) {
- mif_err("hub power func not assinged\n");
+ mif_err("port power func not assinged\n");
return -ENODEV;
}
@@ -136,6 +160,13 @@ static int link_pm_hub_standby(struct link_pm_data *pm_data)
mif_err("hub off fail err=%d\n", err);
pm_data->hub_status = HUB_STATE_OFF;
+
+ /* this function is atomic.
+ * make force disconnect in workqueue..
+ */
+ if (pm_data->usb_ld->if_usb_connected)
+ schedule_work(&usb_ld->disconnect_work);
+
return err;
}
@@ -169,6 +200,7 @@ static long link_pm_ioctl(struct file *file, unsigned int cmd,
{
int value, err = 0;
struct link_pm_data *pm_data = file->private_data;
+ struct usb_link_device *usb_ld = pm_data->usb_ld;
mif_info("cmd: 0x%08x\n", cmd);
@@ -182,15 +214,13 @@ static long link_pm_ioctl(struct file *file, unsigned int cmd,
case IOCTL_LINK_GET_HOSTWAKE:
return !gpio_get_value(pm_data->gpio_link_hostwake);
case IOCTL_LINK_CONNECTED:
- return pm_data->usb_ld->if_usb_connected;
- case IOCTL_LINK_PORT_ON: /* hub only */
+ return usb_ld->if_usb_connected;
+ case IOCTL_LINK_PORT_ON:
/* ignore cp host wakeup irq, set the hub_init_lock when AP try
CP off and release hub_init_lock when CP boot done */
pm_data->hub_init_lock = 0;
- if (pm_data->root_hub) {
- pm_runtime_resume(pm_data->root_hub);
- pm_runtime_forbid(pm_data->root_hub->parent);
- }
+ if (pm_data->root_hub)
+ pm_runtime_get_sync(pm_data->root_hub);
if (pm_data->port_enable) {
err = pm_data->port_enable(2, 1);
if (err < 0) {
@@ -200,17 +230,7 @@ static long link_pm_ioctl(struct file *file, unsigned int cmd,
pm_data->hub_status = HUB_STATE_RESUMMING;
}
break;
- case IOCTL_LINK_PORT_OFF: /* hub only */
- if (pm_data->usb_ld->if_usb_connected) {
- struct usb_device *udev =
- pm_data->usb_ld->usbdev->parent;
- pm_runtime_get_sync(&udev->dev);
- if (udev->state != USB_STATE_NOTATTACHED) {
- usb_force_disconnect(udev);
- pr_info("force disconnect maybe cp-reset!!\n");
- }
- pm_runtime_put_autosuspend(&udev->dev);
- }
+ case IOCTL_LINK_PORT_OFF:
err = link_pm_hub_standby(pm_data);
if (err < 0) {
mif_err("usb3503 active fail\n");
@@ -218,7 +238,6 @@ static long link_pm_ioctl(struct file *file, unsigned int cmd,
}
pm_data->hub_init_lock = 1;
pm_data->hub_handshake_done = 0;
-
break;
default:
break;
@@ -227,6 +246,50 @@ exit:
return err;
}
+static ssize_t show_autosuspend(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ char *p = buf;
+ struct miscdevice *miscdev = dev_get_drvdata(dev);
+ struct link_pm_data *pm_data = container_of(miscdev,
+ struct link_pm_data, miscdev);
+ struct usb_link_device *usb_ld = pm_data->usb_ld;
+
+ p += sprintf(buf, "%s\n", pm_data->autosuspend ? "on" : "off");
+
+ return p - buf;
+}
+
+static ssize_t store_autosuspend(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct miscdevice *miscdev = dev_get_drvdata(dev);
+ struct link_pm_data *pm_data = container_of(miscdev,
+ struct link_pm_data, miscdev);
+ struct usb_link_device *usb_ld = pm_data->usb_ld;
+ struct task_struct *task = get_current();
+ char taskname[TASK_COMM_LEN];
+
+ mif_info("autosuspend: %s: %s(%d)'\n",
+ buf, get_task_comm(taskname, task), task->pid);
+
+ if (!strncmp(buf, "on", 2)) {
+ pm_data->autosuspend = true;
+ if (usb_ld->usbdev)
+ pm_runtime_allow(&usb_ld->usbdev->dev);
+ } else if (!strncmp(buf, "off", 3)) {
+ pm_data->autosuspend = false;
+ if (usb_ld->usbdev)
+ pm_runtime_forbid(&usb_ld->usbdev->dev);
+ }
+
+ return count;
+}
+
+static struct device_attribute attr_autosuspend =
+ __ATTR(autosuspend, S_IRUGO | S_IWUSR,
+ show_autosuspend, store_autosuspend);
+
static int link_pm_open(struct inode *inode, struct file *file)
{
struct link_pm_data *pm_data =
@@ -253,11 +316,13 @@ static int link_pm_notifier_event(struct notifier_block *this,
{
struct link_pm_data *pm_data =
container_of(this, struct link_pm_data, pm_notifier);
+ struct usb_link_device *usb_ld = pm_data->usb_ld;
switch (event) {
case PM_SUSPEND_PREPARE:
pm_data->dpm_suspending = true;
- link_pm_hub_standby(pm_data);
+ if (has_hub(usb_ld))
+ link_pm_hub_standby(pm_data);
return NOTIFY_OK;
case PM_POST_SUSPEND:
pm_data->dpm_suspending = false;
@@ -286,12 +351,12 @@ int link_pm_init(struct usb_link_device *usb_ld, void *data)
pm_data->gpio_link_slavewake = pm_pdata->gpio_link_slavewake;
pm_data->link_reconnect = pm_pdata->link_reconnect;
pm_data->port_enable = pm_pdata->port_enable;
- pm_data->cpufreq_lock = pm_pdata->cpufreq_lock;
- pm_data->cpufreq_unlock = pm_pdata->cpufreq_unlock;
+ pm_data->freq_lock = pm_pdata->freq_lock;
+ pm_data->freq_unlock = pm_pdata->freq_unlock;
pm_data->autosuspend_delay_ms = pm_pdata->autosuspend_delay_ms;
+ pm_data->autosuspend = true;
pm_data->usb_ld = usb_ld;
- pm_data->link_pm_active = false;
usb_ld->link_pm_data = pm_data;
pm_data->miscdev.minor = MISC_DYNAMIC_MINOR;
@@ -304,6 +369,13 @@ int link_pm_init(struct usb_link_device *usb_ld, void *data)
goto err_misc_register;
}
+ err = device_create_file(pm_data->miscdev.this_device,
+ &attr_autosuspend);
+ if (err) {
+ mif_err("fail to create file: autosuspend: %d\n", err);
+ goto err_create_file;
+ }
+
pm_data->hub_init_lock = 1;
irq = gpio_to_irq(usb_ld->pdata->gpio_host_wakeup);
err = request_threaded_irq(irq, NULL, usb_resume_irq,
@@ -319,12 +391,16 @@ int link_pm_init(struct usb_link_device *usb_ld, void *data)
if (has_hub(usb_ld)) {
init_completion(&pm_data->hub_active);
pm_data->hub_status = HUB_STATE_OFF;
- pm_pdata->p_hub_status = &pm_data->hub_status;
pm_data->hub_handshake_done = 0;
pm_data->root_hub = NULL;
+
+ pm_pdata->hub_standby = link_pm_hub_standby;
+ pm_pdata->hub_pm_data = pm_data;
+
wake_lock_init(&pm_data->hub_lock, WAKE_LOCK_SUSPEND,
"modem_hub_enum_lock");
INIT_DELAYED_WORK(&pm_data->link_pm_hub, link_pm_hub_work);
+ pm_data->hub_work_running = false;
}
pm_data->pm_notifier.notifier_call = link_pm_notifier_event;
@@ -333,10 +409,9 @@ int link_pm_init(struct usb_link_device *usb_ld, void *data)
return 0;
err_request_irq:
+err_create_file:
misc_deregister(&pm_data->miscdev);
err_misc_register:
kfree(pm_data);
return err;
}
-
-
diff --git a/drivers/misc/modem_if/modem_link_pm_usb.h b/drivers/misc/modem_if/modem_link_pm_usb.h
index 103e4f4..d26af76 100644
--- a/drivers/misc/modem_if/modem_link_pm_usb.h
+++ b/drivers/misc/modem_if/modem_link_pm_usb.h
@@ -41,30 +41,25 @@ struct link_pm_data {
int hub_handshake_done;
struct wake_lock hub_lock;
struct delayed_work link_pm_hub;
+ bool hub_work_running;
int hub_on_retry_cnt;
struct device *root_hub;
- struct delayed_work link_pm_work;
- struct delayed_work link_pm_start;
- struct delayed_work link_reconnect_work;
- bool resume_requested;
- bool link_pm_active;
-
- struct wake_lock l2_wake;
- struct wake_lock boot_wake;
struct notifier_block pm_notifier;
bool dpm_suspending;
int (*port_enable)(int, int);
- int (*cpufreq_lock)(void);
- int (*cpufreq_unlock)(void);
+ int (*freq_lock)(struct device *dev);
+ int (*freq_unlock)(struct device *dev);
int autosuspend_delay_ms; /* if zero, the default value is used */
+ bool autosuspend;
};
bool link_pm_set_active(struct usb_link_device *usb_ld);
bool link_pm_is_connected(struct usb_link_device *usb_ld);
+void link_pm_preactive(struct link_pm_data *pm_data);
int link_pm_init(struct usb_link_device *usb_ld, void *data);
#endif
diff --git a/drivers/misc/modem_if/modem_modemctl_device_cbp72.c b/drivers/misc/modem_if/modem_modemctl_device_cbp72.c
index b8d2711..2617be8 100644
--- a/drivers/misc/modem_if/modem_modemctl_device_cbp72.c
+++ b/drivers/misc/modem_if/modem_modemctl_device_cbp72.c
@@ -43,8 +43,10 @@ static irqreturn_t phone_active_handler(int irq, void *arg)
phone_state, phone_reset, phone_active);
if (phone_reset && phone_active) {
- phone_state = STATE_ONLINE;
- mc->bootd->modem_state_changed(mc->bootd, phone_state);
+ if (mc->phone_state == STATE_BOOTING) {
+ phone_state = STATE_ONLINE;
+ mc->bootd->modem_state_changed(mc->bootd, phone_state);
+ }
} else if (phone_reset && !phone_active) {
if (mc->phone_state == STATE_ONLINE) {
phone_state = STATE_CRASH_EXIT;
@@ -70,6 +72,10 @@ static int cbp72_on(struct modem_ctl *mc)
{
mif_info("start!!!\n");
+ /* prevent sleep during bootloader downloading */
+ if (!wake_lock_active(&mc->mc_wake_lock))
+ wake_lock(&mc->mc_wake_lock);
+
gpio_set_value(mc->gpio_cp_on, 0);
if (mc->gpio_cp_off)
gpio_set_value(mc->gpio_cp_off, 1);
@@ -178,6 +184,9 @@ static int cbp72_boot_off(struct modem_ctl *mc)
return -ENXIO;
}
mc->bootd->modem_state_changed(mc->bootd, STATE_ONLINE);
+
+ wake_unlock(&mc->mc_wake_lock);
+
return 0;
}
@@ -253,6 +262,8 @@ int cbp72_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata)
return ret;
}
+ wake_lock_init(&mc->mc_wake_lock, WAKE_LOCK_SUSPEND, "cbp72_wake_lock");
+
ret = enable_irq_wake(irq);
if (ret)
mif_err("enable_irq_wake fail (%d)\n", ret);
diff --git a/drivers/misc/modem_if/modem_modemctl_device_cmc221.c b/drivers/misc/modem_if/modem_modemctl_device_cmc221.c
index 2d564e9..fe8ce69 100644
--- a/drivers/misc/modem_if/modem_modemctl_device_cmc221.c
+++ b/drivers/misc/modem_if/modem_modemctl_device_cmc221.c
@@ -30,9 +30,9 @@
#define PIF_TIMEOUT (180 * HZ)
#define DPRAM_INIT_TIMEOUT (30 * HZ)
-
static void mc_state_fsm(struct modem_ctl *mc)
{
+ struct link_device *ld = get_current_link(mc->iod);
int cp_on = gpio_get_value(mc->gpio_cp_on);
int cp_reset = gpio_get_value(mc->gpio_cp_reset);
int cp_active = gpio_get_value(mc->gpio_phone_active);
@@ -46,6 +46,7 @@ static void mc_state_fsm(struct modem_ctl *mc)
if (!cp_on) {
gpio_set_value(mc->gpio_cp_reset, 0);
new_state = STATE_OFFLINE;
+ ld->mode = LINK_MODE_OFFLINE;
mif_err("%s: new_state = PHONE_PWR_OFF\n", mc->name);
} else if (old_state == STATE_ONLINE) {
new_state = STATE_CRASH_EXIT;
@@ -55,7 +56,6 @@ static void mc_state_fsm(struct modem_ctl *mc)
}
}
-exit:
if (old_state != new_state) {
mc->bootd->modem_state_changed(mc->bootd, new_state);
mc->iod->modem_state_changed(mc->iod, new_state);
@@ -73,37 +73,53 @@ static irqreturn_t phone_active_handler(int irq, void *arg)
return IRQ_HANDLED;
}
+/* TX dynamic switching between DPRAM and USB in one modem */
static irqreturn_t dynamic_switching_handler(int irq, void *arg)
{
struct modem_ctl *mc = (struct modem_ctl *)arg;
int txpath = gpio_get_value(mc->gpio_dynamic_switching);
+ bool enumerated = usb_is_enumerated(mc->msd);
+
+ mif_err("txpath=%d, enumeration=%d\n", txpath, enumerated);
+
+ /* do not switch to USB, when USB is not enumerated. */
+ if (!enumerated && txpath) {
+ mc->need_switch_to_usb = true;
+ return IRQ_HANDLED;
+ }
- rawdevs_set_tx_link(&mc->commons, txpath ? LINKDEV_USB : LINKDEV_DPRAM);
+ mc->need_switch_to_usb = false;
+ rawdevs_set_tx_link(mc->msd, txpath ? LINKDEV_USB : LINKDEV_DPRAM);
return IRQ_HANDLED;
}
static int cmc221_on(struct modem_ctl *mc)
{
- struct link_device *ld = get_current_link(mc->bootd);
+ struct link_device *ld = get_current_link(mc->iod);
+
+ if (!wake_lock_active(&mc->mc_wake_lock))
+ wake_lock(&mc->mc_wake_lock);
+ set_sromc_access(true);
+
+ mc->phone_state = STATE_OFFLINE;
+ ld->mode = LINK_MODE_OFFLINE;
- mif_err("%s\n", mc->bootd->name);
+ mif_err("%s\n", mc->name);
disable_irq_nosync(mc->irq_phone_active);
- gpio_set_value(mc->gpio_cp_reset, 0);
gpio_set_value(mc->gpio_cp_on, 0);
+ msleep(100);
+ gpio_set_value(mc->gpio_cp_reset, 0);
msleep(500);
gpio_set_value(mc->gpio_cp_on, 1);
-
msleep(100);
gpio_set_value(mc->gpio_cp_reset, 1);
- mc->phone_state = STATE_OFFLINE;
-
return 0;
}
@@ -111,13 +127,14 @@ static int cmc221_off(struct modem_ctl *mc)
{
int cp_on = gpio_get_value(mc->gpio_cp_on);
+ mif_err("%s\n", mc->name);
+
if (mc->phone_state == STATE_OFFLINE || cp_on == 0)
return 0;
- mif_err("%s\n", mc->bootd->name);
-
- if (mc->log_fp)
- mif_close_log_file(mc);
+ if (!wake_lock_active(&mc->mc_wake_lock))
+ wake_lock(&mc->mc_wake_lock);
+ set_sromc_access(true);
gpio_set_value(mc->gpio_cp_on, 0);
@@ -128,7 +145,7 @@ static int cmc221_force_crash_exit(struct modem_ctl *mc)
{
struct link_device *ld = get_current_link(mc->bootd);
- mif_err("%s\n", mc->bootd->name);
+ mif_err("%s\n", mc->name);
/* Make DUMP start */
ld->force_dump(ld, mc->bootd);
@@ -138,10 +155,11 @@ static int cmc221_force_crash_exit(struct modem_ctl *mc)
static int cmc221_dump_reset(struct modem_ctl *mc)
{
- mif_err("%s\n", mc->bootd->name);
+ mif_err("%s\n", mc->name);
- if (mc->log_fp)
- mif_close_log_file(mc);
+ if (!wake_lock_active(&mc->mc_wake_lock))
+ wake_lock(&mc->mc_wake_lock);
+ set_sromc_access(true);
gpio_set_value(mc->gpio_host_active, 0);
gpio_set_value(mc->gpio_cp_reset, 0);
@@ -157,7 +175,7 @@ static int cmc221_dump_reset(struct modem_ctl *mc)
static int cmc221_reset(struct modem_ctl *mc)
{
- mif_err("%s\n", mc->bootd->name);
+ mif_err("%s\n", mc->name);
if (cmc221_off(mc))
return -ENXIO;
@@ -172,13 +190,13 @@ static int cmc221_reset(struct modem_ctl *mc)
static int cmc221_boot_on(struct modem_ctl *mc)
{
- mif_err("%s\n", mc->bootd->name);
+ mif_err("%s\n", mc->name);
+
+ gpio_set_value(mc->gpio_pda_active, 1);
mc->bootd->modem_state_changed(mc->bootd, STATE_BOOTING);
mc->iod->modem_state_changed(mc->iod, STATE_BOOTING);
- mif_set_log_level(mc);
-
return 0;
}
@@ -188,7 +206,7 @@ static int cmc221_boot_off(struct modem_ctl *mc)
struct link_device *ld = get_current_link(mc->bootd);
struct dpram_link_device *dpld = to_dpram_link_device(ld);
- mif_err("%s\n", mc->bootd->name);
+ mif_err("%s\n", mc->name);
ret = wait_for_completion_interruptible_timeout(&dpld->dpram_init_cmd,
DPRAM_INIT_TIMEOUT);
@@ -198,14 +216,18 @@ static int cmc221_boot_off(struct modem_ctl *mc)
return -ENXIO;
}
- if (!mc->fs_ready)
- mc->fs_ready = true;
+ enable_irq(mc->irq_phone_active);
- if (mc->use_mif_log && mc->log_level && !mc->fs_failed &&
- mc->fs_ready && !mc->log_fp)
- mif_open_log_file(mc);
+ return 0;
+}
- enable_irq(mc->irq_phone_active);
+static int cmc221_boot_done(struct modem_ctl *mc)
+{
+ mif_err("%s\n", mc->name);
+
+ set_sromc_access(false);
+ if (wake_lock_active(&mc->mc_wake_lock))
+ wake_unlock(&mc->mc_wake_lock);
return 0;
}
@@ -217,6 +239,7 @@ static void cmc221_get_ops(struct modem_ctl *mc)
mc->ops.modem_reset = cmc221_reset;
mc->ops.modem_boot_on = cmc221_boot_on;
mc->ops.modem_boot_off = cmc221_boot_off;
+ mc->ops.modem_boot_done = cmc221_boot_done;
mc->ops.modem_force_crash_exit = cmc221_force_crash_exit;
mc->ops.modem_dump_reset = cmc221_dump_reset;
}
@@ -231,8 +254,8 @@ int cmc221_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata)
mc->gpio_cp_on = pdata->gpio_cp_on;
mc->gpio_cp_reset = pdata->gpio_cp_reset;
mc->gpio_phone_active = pdata->gpio_phone_active;
-#if 0 /*TODO: check the GPIO map*/
mc->gpio_pda_active = pdata->gpio_pda_active;
+#if 0 /*TODO: check the GPIO map*/
mc->gpio_cp_dump_int = pdata->gpio_cp_dump_int;
mc->gpio_flm_uart_sel = pdata->gpio_flm_uart_sel;
mc->gpio_slave_wakeup = pdata->gpio_slave_wakeup;
@@ -240,6 +263,7 @@ int cmc221_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata)
mc->gpio_host_wakeup = pdata->gpio_host_wakeup;
#endif
mc->gpio_dynamic_switching = pdata->gpio_dynamic_switching;
+ mc->need_switch_to_usb = false;
if (!mc->gpio_cp_on || !mc->gpio_cp_reset || !mc->gpio_phone_active) {
mif_err("%s: ERR! no GPIO data\n", mc->name);
@@ -260,6 +284,8 @@ int cmc221_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata)
}
mif_err("%s: PHONE_ACTIVE IRQ# = %d\n", mc->name, mc->irq_phone_active);
+ wake_lock_init(&mc->mc_wake_lock, WAKE_LOCK_SUSPEND, "cmc_wake_lock");
+
flag = IRQF_TRIGGER_FALLING | IRQF_NO_SUSPEND;
irq = mc->irq_phone_active;
ret = request_irq(irq, phone_active_handler, flag, "cmc_active", mc);
diff --git a/drivers/misc/modem_if/modem_modemctl_device_esc6270.c b/drivers/misc/modem_if/modem_modemctl_device_esc6270.c
new file mode 100644
index 0000000..df0ff80
--- /dev/null
+++ b/drivers/misc/modem_if/modem_modemctl_device_esc6270.c
@@ -0,0 +1,339 @@
+/* /linux/drivers/misc/modem_if/modem_modemctl_device_esc6270.c
+ *
+ * Copyright (C) 2010 Google, Inc.
+ * Copyright (C) 2010 Samsung Electronics.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/init.h>
+
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/platform_device.h>
+
+#include <linux/platform_data/modem.h>
+#include "modem_prj.h"
+#include <linux/regulator/consumer.h>
+
+#include <plat/gpio-cfg.h>
+
+#if defined(CONFIG_LINK_DEVICE_DPRAM)
+#include "modem_link_device_dpram.h"
+#elif defined(CONFIG_LINK_DEVICE_PLD)
+#include "modem_link_device_pld.h"
+#endif
+
+#if defined(CONFIG_LINK_DEVICE_DPRAM) || defined(CONFIG_LINK_DEVICE_PLD)
+#include <linux/mfd/max77693.h>
+
+#define PIF_TIMEOUT (180 * HZ)
+#define DPRAM_INIT_TIMEOUT (30 * HZ)
+
+static int esc6270_on(struct modem_ctl *mc)
+{
+ int ret;
+ struct link_device *ld = get_current_link(mc->iod);
+
+ pr_info("[MODEM_IF:ESC] <%s> start!!!\n", __func__);
+
+ if (!mc->gpio_cp_reset) {
+ pr_err("[MODEM_IF:ESC] no gpio data\n");
+ return -ENXIO;
+ }
+
+ if (mc->gpio_reset_req_n)
+ gpio_set_value(mc->gpio_reset_req_n, 1);
+
+ gpio_set_value(mc->gpio_cp_reset, 1);
+ msleep(30);
+
+ gpio_set_value(mc->gpio_cp_on, 1);
+ msleep(500);
+
+ gpio_set_value(mc->gpio_cp_on, 0);
+ msleep(500);
+
+ gpio_set_value(mc->gpio_pda_active, 1);
+
+ mc->iod->modem_state_changed(mc->iod, STATE_BOOTING);
+ ld->mode = LINK_MODE_BOOT;
+
+ return 0;
+}
+
+static int esc6270_off(struct modem_ctl *mc)
+{
+ pr_info("[MODEM_IF:ESC] esc6270_off()\n");
+
+#if 1
+ if (!mc->gpio_cp_reset) {
+ pr_err("[MODEM_IF:ESC] no gpio data\n");
+ return -ENXIO;
+ }
+
+ gpio_set_value(mc->gpio_cp_reset, 0);
+ gpio_set_value(mc->gpio_cp_on, 0);
+#endif
+
+ mc->iod->modem_state_changed(mc->iod, STATE_OFFLINE);
+
+ return 0;
+}
+
+static int esc6270_reset(struct modem_ctl *mc)
+{
+ int ret = 0;
+
+ pr_debug("[MODEM_IF:ESC] esc6270_reset()\n");
+
+ ret = esc6270_off(mc);
+ if (ret)
+ return -ENXIO;
+
+ msleep(100);
+
+ ret = esc6270_on(mc);
+ if (ret)
+ return -ENXIO;
+
+ return 0;
+}
+
+int esc6270_boot_on(struct modem_ctl *mc)
+{
+ struct link_device *ld = get_current_link(mc->iod);
+
+ pr_info("[MODEM_IF:ESC] <%s>\n", __func__);
+
+ /* Need to init uart byt gpio_flm_uart_sel GPIO */
+ if (!mc->gpio_cp_reset || !mc->gpio_flm_uart_sel) {
+ pr_err("[MODEM_IF:ESC] no gpio data\n");
+ return -ENXIO;
+ }
+ gpio_set_value(mc->gpio_flm_uart_sel, 1);
+
+ pr_info(" - ESC_PHONE_ON : %d, ESC_RESET_N : %d\n",
+ gpio_get_value(mc->gpio_cp_on),
+ gpio_get_value(mc->gpio_cp_reset));
+
+ gpio_set_value(mc->gpio_cp_on, 0);
+ gpio_direction_output(mc->gpio_cp_reset, 0);
+ msleep(100);
+
+ gpio_direction_output(mc->gpio_cp_on, 1);
+ msleep(44);
+
+ pr_info(" - ESC_PHONE_ON : %d, ESC_RESET_N : %d\n",
+ gpio_get_value(mc->gpio_cp_on),
+ gpio_get_value(mc->gpio_cp_reset));
+
+ gpio_direction_input(mc->gpio_cp_reset);
+ msleep(600);
+ gpio_direction_output(mc->gpio_cp_on, 0);
+
+ msleep(20);
+ pr_info(" - ESC_PHONE_ON : %d, ESC_RESET_N : %d\n",
+ gpio_get_value(mc->gpio_cp_on),
+ gpio_get_value(mc->gpio_cp_reset));
+
+#if defined(CONFIG_LINK_DEVICE_PLD)
+ gpio_direction_output(mc->gpio_fpga_cs_n, 1);
+#endif
+
+ mc->iod->modem_state_changed(mc->iod, STATE_BOOTING);
+ ld->mode = LINK_MODE_BOOT;
+
+ return 0;
+}
+
+static int esc6270_boot_off(struct modem_ctl *mc)
+{
+ pr_info("[MODEM_IF:ESC] <%s>\n", __func__);
+
+ if (!mc->gpio_flm_uart_sel) {
+ pr_err("[MODEM_IF:ESC] no gpio data\n");
+ return -ENXIO;
+ }
+
+ gpio_set_value(mc->gpio_flm_uart_sel, 0);
+
+ mc->iod->modem_state_changed(mc->iod, STATE_OFFLINE);
+
+ return 0;
+}
+
+static int esc6270_active_count;
+
+static irqreturn_t phone_active_irq_handler(int irq, void *arg)
+{
+ struct modem_ctl *mc = (struct modem_ctl *)arg;
+ int phone_reset = 0;
+ int phone_active = 0;
+ int phone_state = 0;
+ int cp_dump_int = 0;
+
+ if (!mc->gpio_cp_reset ||
+ !mc->gpio_phone_active) { /* || !mc->gpio_cp_dump_int) { */
+ pr_err("[MODEM_IF:ESC] no gpio data\n");
+ return IRQ_HANDLED;
+ }
+
+ phone_reset = gpio_get_value(mc->gpio_cp_reset);
+ phone_active = gpio_get_value(mc->gpio_phone_active);
+ cp_dump_int = gpio_get_value(mc->gpio_cp_dump_int);
+
+ pr_info("[MODEM_IF:ESC] <%s> phone_reset=%d, phone_active=%d, cp_dump_int=%d\n",
+ __func__, phone_reset, phone_active, cp_dump_int);
+
+ if (phone_reset && phone_active) {
+ phone_state = STATE_ONLINE;
+ if (mc->iod && mc->iod->modem_state_changed)
+ mc->iod->modem_state_changed(mc->iod, phone_state);
+ } else if (phone_reset && !phone_active) {
+ if (mc->phone_state == STATE_ONLINE) {
+ if (cp_dump_int)
+ phone_state = STATE_CRASH_EXIT;
+ else
+ phone_state = STATE_CRASH_RESET;
+ if (mc->iod && mc->iod->modem_state_changed)
+ mc->iod->modem_state_changed(mc->iod,
+ phone_state);
+ }
+ } else {
+ phone_state = STATE_OFFLINE;
+ if (mc->iod && mc->iod->modem_state_changed)
+ mc->iod->modem_state_changed(mc->iod, phone_state);
+ }
+
+ if (phone_active)
+ irq_set_irq_type(mc->irq_phone_active, IRQ_TYPE_LEVEL_LOW);
+ else
+ irq_set_irq_type(mc->irq_phone_active, IRQ_TYPE_LEVEL_HIGH);
+
+ pr_info("[MODEM_IF::ESC] <%s> phone_state = %d\n",
+ __func__, phone_state);
+
+ return IRQ_HANDLED;
+}
+
+#if defined(CONFIG_SIM_DETECT)
+static irqreturn_t sim_detect_irq_handler(int irq, void *_mc)
+{
+ struct modem_ctl *mc = (struct modem_ctl *)_mc;
+
+ pr_info("[MODEM_IF:ESC] <%s> gpio_sim_detect = %d\n",
+ __func__, gpio_get_value(mc->gpio_sim_detect));
+
+ if (mc->iod && mc->iod->sim_state_changed)
+ mc->iod->sim_state_changed(mc->iod,
+ !gpio_get_value(mc->gpio_sim_detect));
+
+ return IRQ_HANDLED;
+}
+#endif
+
+static void esc6270_get_ops(struct modem_ctl *mc)
+{
+ mc->ops.modem_on = esc6270_on;
+ mc->ops.modem_off = esc6270_off;
+ mc->ops.modem_reset = esc6270_reset;
+ mc->ops.modem_boot_on = esc6270_boot_on;
+ mc->ops.modem_boot_off = esc6270_boot_off;
+}
+
+int esc6270_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata)
+{
+ int ret = 0;
+ struct platform_device *pdev;
+
+ mc->gpio_cp_on = pdata->gpio_cp_on;
+ mc->gpio_reset_req_n = pdata->gpio_reset_req_n;
+ mc->gpio_cp_reset = pdata->gpio_cp_reset;
+ mc->gpio_pda_active = pdata->gpio_pda_active;
+ mc->gpio_phone_active = pdata->gpio_phone_active;
+ mc->gpio_cp_dump_int = pdata->gpio_cp_dump_int;
+ mc->gpio_flm_uart_sel = pdata->gpio_flm_uart_sel;
+ mc->gpio_cp_warm_reset = pdata->gpio_cp_warm_reset;
+ mc->gpio_sim_detect = pdata->gpio_sim_detect;
+
+#if defined(CONFIG_LINK_DEVICE_PLD)
+ mc->gpio_fpga_cs_n = pdata->gpio_fpga1_cs_n;
+#endif
+
+ gpio_set_value(mc->gpio_cp_reset, 0);
+ gpio_set_value(mc->gpio_cp_on, 0);
+
+ pdev = to_platform_device(mc->dev);
+ mc->irq_phone_active = platform_get_irq_byname(pdev, "cp_active_irq");
+ pr_info("[MODEM_IF:ESC] <%s> PHONE_ACTIVE IRQ# = %d\n",
+ __func__, mc->irq_phone_active);
+
+ esc6270_get_ops(mc);
+
+ if (mc->irq_phone_active) {
+ ret = request_irq(mc->irq_phone_active,
+ phone_active_irq_handler,
+ IRQF_TRIGGER_HIGH,
+ "esc_active",
+ mc);
+ if (ret) {
+ pr_err("[MODEM_IF:ESC] <%s> failed to request_irq IRQ# %d (err=%d)\n",
+ __func__, mc->irq_phone_active, ret);
+ return ret;
+ }
+
+ ret = enable_irq_wake(mc->irq_phone_active);
+ if (ret) {
+ pr_err("[MODEM_IF:ESC] %s: failed to enable_irq_wake IRQ# %d (err=%d)\n",
+ __func__, mc->irq_phone_active, ret);
+ free_irq(mc->irq_phone_active, mc);
+ return ret;
+ }
+ }
+
+#if defined(CONFIG_SIM_DETECT)
+ mc->irq_sim_detect = platform_get_irq_byname(pdev, "sim_irq");
+ pr_info("[MODEM_IF:ESC] <%s> SIM_DECTCT IRQ# = %d\n",
+ __func__, mc->irq_sim_detect);
+
+ if (mc->irq_sim_detect) {
+ ret = request_irq(mc->irq_sim_detect, sim_detect_irq_handler,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ "esc_sim_detect", mc);
+ if (ret) {
+ mif_err("failed to request_irq: %d\n", ret);
+ mc->sim_state.online = false;
+ mc->sim_state.changed = false;
+ return ret;
+ }
+
+ ret = enable_irq_wake(mc->irq_sim_detect);
+ if (ret) {
+ mif_err("failed to enable_irq_wake: %d\n", ret);
+ free_irq(mc->irq_sim_detect, mc);
+ mc->sim_state.online = false;
+ mc->sim_state.changed = false;
+ return ret;
+ }
+
+ /* initialize sim_state => insert: gpio=0, remove: gpio=1 */
+ mc->sim_state.online = !gpio_get_value(mc->gpio_sim_detect);
+ }
+#endif
+
+ return ret;
+}
+#endif
diff --git a/drivers/misc/modem_if/modem_modemctl_device_mdm6600.c b/drivers/misc/modem_if/modem_modemctl_device_mdm6600.c
index 6f1807e..2706e5e 100644
--- a/drivers/misc/modem_if/modem_modemctl_device_mdm6600.c
+++ b/drivers/misc/modem_if/modem_modemctl_device_mdm6600.c
@@ -258,14 +258,72 @@ int mdm6600_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata)
}
#endif /* CONFIG_MACH_U1_KOR_LGT */
-#if defined(CONFIG_MACH_C1CTC) || defined(CONFIG_MACH_M0_CTC)
+#if defined(CONFIG_MACH_M0_CTC) || defined(CONFIG_MACH_T0_CHN_CTC)
+
+#if defined(CONFIG_LINK_DEVICE_DPRAM)
#include "modem_link_device_dpram.h"
+#elif defined(CONFIG_LINK_DEVICE_PLD)
+#include "modem_link_device_pld.h"
+#endif
#define PIF_TIMEOUT (180 * HZ)
#define DPRAM_INIT_TIMEOUT (30 * HZ)
+#if defined(CONFIG_MACH_M0_DUOSCTC) || defined(CONFIG_MACH_M0_GRANDECTC) || \
+ defined(CONFIG_MACH_T0_CHN_CTC)
+static void mdm6600_vbus_on(void)
+{
+ struct regulator *regulator;
+
+ pr_info("[MSM] <%s>\n", __func__);
+
+#if defined(CONFIG_MACH_T0_CHN_CTC)
+ if (system_rev == 4)
+ regulator = regulator_get(NULL, "vcc_1.8v_lcd");
+ else
+ regulator = regulator_get(NULL, "vcc_1.8v_usb");
+#else
+ regulator = regulator_get(NULL, "vusbhub_osc_1.8v");
+#endif
+ if (IS_ERR(regulator)) {
+ pr_err("[MSM] error getting regulator_get <%s>\n", __func__);
+ return ;
+ }
+ regulator_enable(regulator);
+ regulator_put(regulator);
+
+ pr_info("[MSM] <%s> enable\n", __func__);
+}
+
+static void mdm6600_vbus_off(void)
+{
+ struct regulator *regulator;
+
+ pr_info("[MSM] <%s>\n", __func__);
+
+#if defined(CONFIG_MACH_T0_CHN_CTC)
+ if (system_rev == 4)
+ regulator = regulator_get(NULL, "vcc_1.8v_lcd");
+ else
+ regulator = regulator_get(NULL, "vcc_1.8v_usb");
+#else
+ regulator = regulator_get(NULL, "vusbhub_osc_1.8v");
+#endif
+ if (IS_ERR(regulator)) {
+ pr_err("[MSM] error getting regulator_get <%s>\n", __func__);
+ return ;
+ }
+ regulator_disable(regulator);
+ regulator_put(regulator);
+
+ pr_info("[MSM] <%s> disable\n", __func__);
+}
+#endif
+
static int mdm6600_on(struct modem_ctl *mc)
{
+ struct link_device *ld = get_current_link(mc->iod);
+
pr_info("[MSM] <%s>\n", __func__);
if (!mc->gpio_reset_req_n || !mc->gpio_cp_reset
@@ -274,20 +332,28 @@ static int mdm6600_on(struct modem_ctl *mc)
return -ENXIO;
}
- gpio_set_value(mc->gpio_reset_req_n, 1);
-
- gpio_set_value(mc->gpio_cp_reset, 1);
- msleep(30);
+ gpio_set_value(mc->gpio_pda_active, 0);
gpio_set_value(mc->gpio_cp_on, 1);
msleep(500);
+ gpio_set_value(mc->gpio_reset_req_n, 1);
+ msleep(50);
+
+ gpio_set_value(mc->gpio_cp_reset, 1);
+ msleep(50);
+
gpio_set_value(mc->gpio_cp_on, 0);
msleep(500);
gpio_set_value(mc->gpio_pda_active, 1);
+#if defined(CONFIG_LINK_DEVICE_PLD)
+ gpio_set_value(mc->gpio_fpga_cs_n, 1);
+#endif
+
mc->iod->modem_state_changed(mc->iod, STATE_BOOTING);
+ ld->mode = LINK_MODE_BOOT;
return 0;
}
@@ -302,8 +368,11 @@ static int mdm6600_off(struct modem_ctl *mc)
}
gpio_set_value(mc->gpio_cp_reset, 0);
+ gpio_set_value(mc->gpio_reset_req_n, 0);
gpio_set_value(mc->gpio_cp_on, 0);
+ msleep(200);
+
mc->iod->modem_state_changed(mc->iod, STATE_OFFLINE);
return 0;
@@ -312,26 +381,46 @@ static int mdm6600_off(struct modem_ctl *mc)
static int mdm6600_reset(struct modem_ctl *mc)
{
int ret = 0;
+ struct link_device *ld = get_current_link(mc->iod);
pr_info("[MSM] <%s>\n", __func__);
- ret = mdm6600_off(mc);
- if (ret)
+ if (!mc->gpio_reset_req_n || !mc->gpio_cp_reset
+ || !mc->gpio_cp_on || !mc->gpio_pda_active) {
+ pr_err("[MSM] no gpio data\n");
return -ENXIO;
+ }
+
+ gpio_set_value(mc->gpio_cp_reset, 0);
+ gpio_set_value(mc->gpio_reset_req_n, 0);
+ gpio_set_value(mc->gpio_cp_on, 0);
-#if 0
msleep(100);
-#endif
- ret = mdm6600_on(mc);
- if (ret)
- return -ENXIO;
+ gpio_set_value(mc->gpio_cp_on, 1);
+ msleep(300);
+
+ gpio_set_value(mc->gpio_reset_req_n, 1);
+ msleep(50);
+
+ gpio_set_value(mc->gpio_cp_reset, 1);
+ msleep(50);
+
+ gpio_set_value(mc->gpio_cp_on, 0);
+ msleep(100);
+
+ gpio_set_value(mc->gpio_pda_active, 1);
+
+ mc->iod->modem_state_changed(mc->iod, STATE_BOOTING);
+ ld->mode = LINK_MODE_BOOT;
return 0;
}
static int mdm6600_boot_on(struct modem_ctl *mc)
{
+ struct regulator *regulator;
+
pr_info("[MSM] <%s>\n", __func__);
if (!mc->gpio_flm_uart_sel) {
@@ -339,62 +428,106 @@ static int mdm6600_boot_on(struct modem_ctl *mc)
return -ENXIO;
}
- pr_info("[MSM] <%s> %s\n", __func__, "USB_BOOT_EN initializing");
+#if defined(CONFIG_MACH_M0_DUOSCTC) || defined(CONFIG_MACH_T0_CHN_CTC)
+ mdm6600_vbus_on();
+#elif defined(CONFIG_MACH_M0_GRANDECTC)
+ if (system_rev >= 14)
+ mdm6600_vbus_on();
+#endif
+ pr_info("[MSM] <%s> %s\n", __func__, "USB_BOOT_EN initializing");
if (system_rev < 11) {
gpio_direction_output(GPIO_USB_BOOT_EN, 0);
s3c_gpio_setpull(GPIO_USB_BOOT_EN, S3C_GPIO_PULL_NONE);
gpio_set_value(GPIO_USB_BOOT_EN, 0);
+ gpio_direction_output(GPIO_BOOT_SW_SEL, 0);
+ s3c_gpio_setpull(GPIO_BOOT_SW_SEL, S3C_GPIO_PULL_NONE);
+ gpio_set_value(GPIO_BOOT_SW_SEL, 0);
+
+ msleep(100);
+
gpio_direction_output(GPIO_USB_BOOT_EN, 1);
gpio_set_value(GPIO_USB_BOOT_EN, 1);
pr_info("[MSM] <%s> USB_BOOT_EN:[%d]\n", __func__,
gpio_get_value(GPIO_USB_BOOT_EN));
+
+ gpio_direction_output(GPIO_BOOT_SW_SEL, 1);
+ gpio_set_value(GPIO_BOOT_SW_SEL, 1);
+
+ pr_info("[MSM] <%s> BOOT_SW_SEL : [%d]\n", __func__,
+ gpio_get_value(GPIO_BOOT_SW_SEL));
} else if (system_rev == 11) {
gpio_direction_output(GPIO_USB_BOOT_EN, 0);
s3c_gpio_setpull(GPIO_USB_BOOT_EN, S3C_GPIO_PULL_NONE);
gpio_set_value(GPIO_USB_BOOT_EN, 0);
+ gpio_direction_output(GPIO_USB_BOOT_EN_REV06, 0);
+ s3c_gpio_setpull(GPIO_USB_BOOT_EN_REV06, S3C_GPIO_PULL_NONE);
+ gpio_set_value(GPIO_USB_BOOT_EN_REV06, 0);
+
+ msleep(100);
+
gpio_direction_output(GPIO_USB_BOOT_EN, 1);
gpio_set_value(GPIO_USB_BOOT_EN, 1);
pr_info("[MSM] <%s> USB_BOOT_EN:[%d]\n", __func__,
gpio_get_value(GPIO_USB_BOOT_EN));
- gpio_direction_output(GPIO_USB_BOOT_EN_REV06, 0);
- s3c_gpio_setpull(GPIO_USB_BOOT_EN_REV06, S3C_GPIO_PULL_NONE);
- gpio_set_value(GPIO_USB_BOOT_EN_REV06, 0);
-
gpio_direction_output(GPIO_USB_BOOT_EN_REV06, 1);
gpio_set_value(GPIO_USB_BOOT_EN_REV06, 1);
- pr_info("[MSM] <%s> USB_BOOT_EN:[%d]\n", __func__,
- gpio_get_value(GPIO_USB_BOOT_EN_REV06));
+ pr_info("[MSM(%d)] <%s> USB_BOOT_EN:[%d]\n", system_rev,
+ __func__, gpio_get_value(GPIO_USB_BOOT_EN_REV06));
+
+ gpio_direction_output(GPIO_BOOT_SW_SEL, 0);
+ s3c_gpio_setpull(GPIO_BOOT_SW_SEL, S3C_GPIO_PULL_NONE);
+ gpio_set_value(GPIO_BOOT_SW_SEL, 0);
+
+ gpio_direction_output(GPIO_BOOT_SW_SEL_REV06, 0);
+ s3c_gpio_setpull(GPIO_BOOT_SW_SEL_REV06, S3C_GPIO_PULL_NONE);
+ gpio_set_value(GPIO_BOOT_SW_SEL_REV06, 0);
+
+ msleep(100);
+
+ gpio_direction_output(GPIO_BOOT_SW_SEL, 1);
+ gpio_set_value(GPIO_BOOT_SW_SEL, 1);
+
+ pr_info("[MSM] <%s> BOOT_SW_SEL : [%d]\n", __func__,
+ gpio_get_value(GPIO_BOOT_SW_SEL));
+
+ gpio_direction_output(GPIO_BOOT_SW_SEL_REV06, 1);
+ gpio_set_value(GPIO_BOOT_SW_SEL_REV06, 1);
+
+ pr_info("[MSM(%d)] <%s> BOOT_SW_SEL : [%d]\n", system_rev,
+ __func__, gpio_get_value(GPIO_BOOT_SW_SEL_REV06));
} else { /* system_rev>11 */
gpio_direction_output(GPIO_USB_BOOT_EN_REV06, 0);
s3c_gpio_setpull(GPIO_USB_BOOT_EN_REV06, S3C_GPIO_PULL_NONE);
gpio_set_value(GPIO_USB_BOOT_EN_REV06, 0);
+ gpio_direction_output(GPIO_BOOT_SW_SEL_REV06, 0);
+ s3c_gpio_setpull(GPIO_BOOT_SW_SEL_REV06, S3C_GPIO_PULL_NONE);
+ gpio_set_value(GPIO_BOOT_SW_SEL_REV06, 0);
+
+ msleep(100);
+
gpio_direction_output(GPIO_USB_BOOT_EN_REV06, 1);
gpio_set_value(GPIO_USB_BOOT_EN_REV06, 1);
pr_info("[MSM] <%s> USB_BOOT_EN:[%d]\n", __func__,
gpio_get_value(GPIO_USB_BOOT_EN_REV06));
- }
-
- gpio_direction_output(mc->gpio_flm_uart_sel, 0);
- s3c_gpio_setpull(mc->gpio_flm_uart_sel, S3C_GPIO_PULL_NONE);
- gpio_set_value(mc->gpio_flm_uart_sel, 0);
-
- gpio_direction_output(mc->gpio_flm_uart_sel, 1);
- gpio_set_value(mc->gpio_flm_uart_sel, 1);
+ gpio_direction_output(GPIO_BOOT_SW_SEL_REV06, 1);
+ gpio_set_value(GPIO_BOOT_SW_SEL_REV06, 1);
pr_info("[MSM] <%s> BOOT_SW_SEL : [%d]\n", __func__,
- gpio_get_value(mc->gpio_flm_uart_sel));
+ gpio_get_value(GPIO_BOOT_SW_SEL_REV06));
+
+ }
mc->iod->modem_state_changed(mc->iod, STATE_BOOTING);
@@ -405,11 +538,22 @@ static int mdm6600_boot_off(struct modem_ctl *mc)
{
pr_info("[MSM] <%s>\n", __func__);
- if (!mc->gpio_flm_uart_sel || !mc->gpio_flm_uart_sel_rev06) {
+ if (!mc->gpio_flm_uart_sel
+#if defined(CONFIG_MACH_M0_CTC)
+ || !mc->gpio_flm_uart_sel_rev06
+#endif
+ ) {
pr_err("[MSM] no gpio data\n");
return -ENXIO;
}
+#if defined(CONFIG_MACH_M0_DUOSCTC) || defined(CONFIG_MACH_T0_CHN_CTC)
+ mdm6600_vbus_off();
+#elif defined(CONFIG_MACH_M0_GRANDECTC)
+ if (system_rev >= 14)
+ mdm6600_vbus_off();
+#endif
+
if (system_rev < 11) {
gpio_direction_output(GPIO_USB_BOOT_EN, 0);
s3c_gpio_setpull(GPIO_USB_BOOT_EN, S3C_GPIO_PULL_NONE);
@@ -422,19 +566,23 @@ static int mdm6600_boot_off(struct modem_ctl *mc)
gpio_direction_output(GPIO_USB_BOOT_EN, 0);
s3c_gpio_setpull(GPIO_USB_BOOT_EN, S3C_GPIO_PULL_NONE);
gpio_set_value(GPIO_USB_BOOT_EN, 0);
- gpio_direction_output(GPIO_USB_BOOT_EN_REV06, 0);
- s3c_gpio_setpull(GPIO_USB_BOOT_EN_REV06, S3C_GPIO_PULL_NONE);
- gpio_set_value(GPIO_USB_BOOT_EN_REV06, 0);
gpio_direction_output(GPIO_BOOT_SW_SEL, 0);
s3c_gpio_setpull(GPIO_BOOT_SW_SEL, S3C_GPIO_PULL_NONE);
gpio_set_value(GPIO_BOOT_SW_SEL, 0);
+#if defined(CONFIG_MACH_M0_CTC)
+ gpio_direction_output(GPIO_USB_BOOT_EN_REV06, 0);
+ s3c_gpio_setpull(GPIO_USB_BOOT_EN_REV06, S3C_GPIO_PULL_NONE);
+ gpio_set_value(GPIO_USB_BOOT_EN_REV06, 0);
+
gpio_direction_output(GPIO_BOOT_SW_SEL_REV06, 0);
s3c_gpio_setpull(GPIO_BOOT_SW_SEL_REV06, S3C_GPIO_PULL_NONE);
gpio_set_value(GPIO_BOOT_SW_SEL_REV06, 0);
+#endif
} else { /* system_rev>11 */
+#if defined(CONFIG_MACH_M0_CTC)
gpio_direction_output(GPIO_USB_BOOT_EN_REV06, 0);
s3c_gpio_setpull(GPIO_USB_BOOT_EN_REV06, S3C_GPIO_PULL_NONE);
gpio_set_value(GPIO_USB_BOOT_EN_REV06, 0);
@@ -442,9 +590,10 @@ static int mdm6600_boot_off(struct modem_ctl *mc)
gpio_direction_output(GPIO_BOOT_SW_SEL_REV06, 0);
s3c_gpio_setpull(GPIO_BOOT_SW_SEL_REV06, S3C_GPIO_PULL_NONE);
gpio_set_value(GPIO_BOOT_SW_SEL_REV06, 0);
-
+#endif
}
+#if defined(CONFIG_MACH_M0_CTC)
if (max7693_muic_cp_usb_state()) {
msleep(30);
gpio_direction_output(GPIO_USB_BOOT_EN, 1);
@@ -454,8 +603,29 @@ static int mdm6600_boot_off(struct modem_ctl *mc)
s3c_gpio_setpull(GPIO_USB_BOOT_EN_REV06, S3C_GPIO_PULL_NONE);
gpio_set_value(GPIO_USB_BOOT_EN_REV06, 1);
}
+#endif
+
+ gpio_set_value(GPIO_BOOT_SW_SEL, 0);
+
+ return 0;
+}
+
+
+static int mdm6600_force_crash_exit(struct modem_ctl *mc)
+{
+ pr_info("[MSM] <%s>\n", __func__);
+
+ if (!mc->gpio_cp_reset || !mc->gpio_cp_on) {
+ pr_err("[MSM] no gpio data\n");
+ return -ENXIO;
+ }
+
+ s3c_gpio_cfgpin(mc->gpio_cp_dump_int, S3C_GPIO_OUTPUT);
+ gpio_direction_output(mc->gpio_cp_dump_int, 1);
- gpio_set_value(mc->gpio_flm_uart_sel, 0);
+ gpio_set_value(mc->gpio_cp_reset, 0);
+ gpio_set_value(mc->gpio_cp_on, 0);
+ gpio_set_value(mc->gpio_cp_reset, 1);
return 0;
}
@@ -466,16 +636,20 @@ static irqreturn_t phone_active_irq_handler(int irq, void *arg)
int phone_reset = 0;
int phone_active = 0;
int phone_state = 0;
+ int cp_dump_int = 0;
- if (!mc->gpio_cp_reset || !mc->gpio_phone_active) {
+ if (!mc->gpio_cp_reset ||
+ !mc->gpio_phone_active || !mc->gpio_cp_dump_int) {
pr_err("[MSM] no gpio data\n");
return IRQ_HANDLED;
}
phone_reset = gpio_get_value(mc->gpio_cp_reset);
phone_active = gpio_get_value(mc->gpio_phone_active);
- pr_info("[MSM] <%s> phone_reset = %d, phone_active = %d\n",
- __func__, phone_reset, phone_active);
+ cp_dump_int = gpio_get_value(mc->gpio_cp_dump_int);
+
+ pr_info("[MSM] <%s> phone_reset=%d, phone_active=%d, cp_dump_int=%d\n",
+ __func__, phone_reset, phone_active, cp_dump_int);
if (phone_reset && phone_active) {
phone_state = STATE_ONLINE;
@@ -483,7 +657,10 @@ static irqreturn_t phone_active_irq_handler(int irq, void *arg)
mc->iod->modem_state_changed(mc->iod, phone_state);
} else if (phone_reset && !phone_active) {
if (mc->phone_state == STATE_ONLINE) {
- phone_state = STATE_CRASH_EXIT;
+ if (cp_dump_int)
+ phone_state = STATE_CRASH_EXIT;
+ else
+ phone_state = STATE_CRASH_RESET;
if (mc->iod && mc->iod->modem_state_changed)
mc->iod->modem_state_changed(mc->iod,
phone_state);
@@ -504,6 +681,22 @@ static irqreturn_t phone_active_irq_handler(int irq, void *arg)
return IRQ_HANDLED;
}
+#if defined(CONFIG_SIM_DETECT)
+static irqreturn_t sim_detect_irq_handler(int irq, void *_mc)
+{
+ struct modem_ctl *mc = (struct modem_ctl *)_mc;
+
+ pr_info("[MSM] <%s> gpio_sim_detect = %d\n",
+ __func__, gpio_get_value(mc->gpio_sim_detect));
+
+ if (mc->iod && mc->iod->sim_state_changed)
+ mc->iod->sim_state_changed(mc->iod,
+ !gpio_get_value(mc->gpio_sim_detect));
+
+ return IRQ_HANDLED;
+}
+#endif
+
static void mdm6600_get_ops(struct modem_ctl *mc)
{
mc->ops.modem_on = mdm6600_on;
@@ -511,6 +704,7 @@ static void mdm6600_get_ops(struct modem_ctl *mc)
mc->ops.modem_reset = mdm6600_reset;
mc->ops.modem_boot_on = mdm6600_boot_on;
mc->ops.modem_boot_off = mdm6600_boot_off;
+ mc->ops.modem_force_crash_exit = mdm6600_force_crash_exit;
}
int mdm6600_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata)
@@ -525,10 +719,15 @@ int mdm6600_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata)
mc->gpio_phone_active = pdata->gpio_phone_active;
mc->gpio_cp_dump_int = pdata->gpio_cp_dump_int;
mc->gpio_flm_uart_sel = pdata->gpio_flm_uart_sel;
-#if 1
+#if defined(CONFIG_MACH_M0_CTC)
mc->gpio_flm_uart_sel_rev06 = pdata->gpio_flm_uart_sel_rev06;
#endif
mc->gpio_cp_warm_reset = pdata->gpio_cp_warm_reset;
+ mc->gpio_sim_detect = pdata->gpio_sim_detect;
+
+#if defined(CONFIG_LINK_DEVICE_PLD)
+ mc->gpio_fpga_cs_n = pdata->gpio_fpga2_cs_n;
+#endif
gpio_set_value(mc->gpio_cp_reset, 0);
gpio_set_value(mc->gpio_cp_on, 0);
@@ -557,6 +756,36 @@ int mdm6600_init_modemctl_device(struct modem_ctl *mc, struct modem_data *pdata)
return ret;
}
+#if defined(CONFIG_SIM_DETECT)
+ mc->irq_sim_detect = platform_get_irq_byname(pdev, "sim_irq");
+ pr_info("[MSM] <%s> SIM_DECTCT IRQ# = %d\n",
+ __func__, mc->irq_sim_detect);
+
+ if (mc->irq_sim_detect) {
+ ret = request_irq(mc->irq_sim_detect, sim_detect_irq_handler,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ "msm_sim_detect", mc);
+ if (ret) {
+ mif_err("[MSM] failed to request_irq: %d\n", ret);
+ mc->sim_state.online = false;
+ mc->sim_state.changed = false;
+ return ret;
+ }
+
+ ret = enable_irq_wake(mc->irq_sim_detect);
+ if (ret) {
+ mif_err("[MSM] failed to enable_irq_wake: %d\n", ret);
+ free_irq(mc->irq_sim_detect, mc);
+ mc->sim_state.online = false;
+ mc->sim_state.changed = false;
+ return ret;
+ }
+
+ /* initialize sim_state => insert: gpio=0, remove: gpio=1 */
+ mc->sim_state.online = !gpio_get_value(mc->gpio_sim_detect);
+ }
+#endif
+
return ret;
}
#endif
diff --git a/drivers/misc/modem_if/modem_modemctl_device_sprd8803.c b/drivers/misc/modem_if/modem_modemctl_device_sprd8803.c
new file mode 100644
index 0000000..9db864de
--- /dev/null
+++ b/drivers/misc/modem_if/modem_modemctl_device_sprd8803.c
@@ -0,0 +1,230 @@
+/* /linux/drivers/misc/modem_if/modem_modemctl_device_sprd8803.c
+ *
+ * Copyright (C) 2010 Google, Inc.
+ * Copyright (C) 2010 Samsung Electronics.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/cma.h>
+#include <plat/devs.h>
+#include <linux/platform_data/modem.h>
+#include "modem_prj.h"
+#include <plat/gpio-cfg.h>
+
+int sprd_boot_done;
+extern int spi_thread_restart(void);
+
+static int sprd8803_on(struct modem_ctl *mc)
+{
+ pr_debug("[MODEM_IF] %s\n", __func__);
+
+ if (!mc->gpio_cp_on || !mc->gpio_pda_active) {
+ pr_err("[MODEM_IF] no gpio data\n");
+ return -ENXIO;
+ }
+
+ s3c_gpio_cfgpin(EXYNOS4_GPA1(4), S3C_GPIO_SFN(2));
+ s3c_gpio_cfgpin(EXYNOS4_GPA1(5), S3C_GPIO_SFN(2));
+
+#ifdef CONFIG_SEC_DUAL_MODEM_MODE
+ gpio_set_value(mc->gpio_sim_io_sel, 1);
+ gpio_set_value(mc->gpio_cp_ctrl1, 0);
+ gpio_set_value(mc->gpio_cp_ctrl2, 1);
+#endif
+ gpio_set_value(mc->gpio_cp_on, 0);
+ gpio_set_value(mc->gpio_pda_active, 0);
+ msleep(100);
+ gpio_set_value(mc->gpio_cp_on, 1);
+ gpio_set_value(mc->gpio_pda_active, 1);
+
+ mc->phone_state = STATE_BOOTING;
+
+ return 0;
+}
+
+static int sprd8803_off(struct modem_ctl *mc)
+{
+ pr_debug("[MODEM_IF] %s\n", __func__);
+
+ if (!mc->gpio_cp_on) {
+ mif_err("no gpio data\n");
+ return -ENXIO;
+ }
+
+ gpio_set_value(mc->gpio_cp_on, 0);
+
+ mc->phone_state = STATE_OFFLINE;
+
+ return 0;
+}
+
+static int sprd8803_reset(struct modem_ctl *mc)
+{
+ pr_debug("[MODEM_IF] %s\n", __func__);
+
+ spi_thread_restart();
+
+ return 0;
+}
+
+static int sprd8803_boot_on(struct modem_ctl *mc)
+{
+ pr_debug("[MODEM_IF] %s %d\n", __func__, sprd_boot_done);
+ return sprd_boot_done;
+}
+
+static int sprd8803_boot_off(struct modem_ctl *mc)
+{
+ pr_debug("[MODEM_IF] %s\n", __func__);
+ spi_sema_init();
+ return 0;
+}
+
+static int sprd8803_dump_reset(struct modem_ctl *mc)
+{
+ pr_debug("[MODEM_IF] %s\n", __func__);
+
+ if (!mc->gpio_ap_cp_int2)
+ return -ENXIO;
+
+ gpio_set_value(mc->gpio_ap_cp_int2, 0);
+ mc->phone_state = STATE_OFFLINE;
+ msleep(100);
+ gpio_set_value(mc->gpio_ap_cp_int2, 1);
+
+ return 0;
+}
+
+static irqreturn_t phone_active_irq_handler(int irq, void *_mc)
+{
+ int phone_reset = 0;
+ int phone_active_value = 0;
+ int cp_dump_value = 0;
+ int phone_state = 0;
+ struct modem_ctl *mc = (struct modem_ctl *)_mc;
+
+ disable_irq_nosync(mc->irq_phone_active);
+ disable_irq_nosync(gpio_to_irq(mc->gpio_cp_dump_int));
+
+ if (!mc->gpio_phone_active ||
+ !mc->gpio_cp_dump_int) {
+ pr_err("[MODEM_IF] no gpio data\n");
+ goto exit;
+ }
+
+ if (!sprd_boot_done)
+ goto exit;
+
+ phone_active_value = gpio_get_value(mc->gpio_phone_active);
+ cp_dump_value = gpio_get_value(mc->gpio_cp_dump_int);
+
+ pr_err("PA EVENT : pa=%d, cp_dump=%d\n",
+ phone_active_value, cp_dump_value);
+
+ if (phone_active_value)
+ phone_state = STATE_ONLINE;
+ else
+ phone_state = STATE_OFFLINE;
+
+ if (phone_active_value && cp_dump_value)
+ phone_state = STATE_CRASH_EXIT;
+
+ if (mc->iod && mc->iod->modem_state_changed)
+ mc->iod->modem_state_changed(mc->iod, phone_state);
+
+ if (mc->bootd && mc->bootd->modem_state_changed)
+ mc->bootd->modem_state_changed(mc->bootd, phone_state);
+
+exit:
+ enable_irq(mc->irq_phone_active);
+ enable_irq(gpio_to_irq(mc->gpio_cp_dump_int));
+
+ return IRQ_HANDLED;
+}
+
+static void sprd8803_get_ops(struct modem_ctl *mc)
+{
+ mc->ops.modem_on = sprd8803_on;
+ mc->ops.modem_off = sprd8803_off;
+ mc->ops.modem_reset = sprd8803_reset;
+ mc->ops.modem_boot_on = sprd8803_boot_on;
+ mc->ops.modem_boot_off = sprd8803_boot_off;
+ mc->ops.modem_dump_reset = sprd8803_dump_reset;
+ mc->ops.modem_force_crash_exit = sprd8803_dump_reset;
+}
+
+int sprd8803_init_modemctl_device(struct modem_ctl *mc,
+ struct modem_data *pdata)
+{
+ int ret = 0;
+ int irq_cp_dump_int;
+ struct platform_device *pdev;
+
+ mc->gpio_cp_on = pdata->gpio_cp_on;
+ mc->gpio_pda_active = pdata->gpio_pda_active;
+ mc->gpio_phone_active = pdata->gpio_phone_active;
+ mc->gpio_cp_dump_int = pdata->gpio_cp_dump_int;
+ mc->gpio_ap_cp_int1 = pdata->gpio_ap_cp_int1;
+ mc->gpio_ap_cp_int2 = pdata->gpio_ap_cp_int2;
+
+#ifdef CONFIG_SEC_DUAL_MODEM_MODE
+ mc->gpio_sim_io_sel = pdata->gpio_sim_io_sel;
+ mc->gpio_cp_ctrl1 = pdata->gpio_cp_ctrl1;
+ mc->gpio_cp_ctrl2 = pdata->gpio_cp_ctrl2;
+#endif
+
+
+ pdev = to_platform_device(mc->dev);
+ mc->irq_phone_active = gpio_to_irq(mc->gpio_phone_active);
+ irq_cp_dump_int = gpio_to_irq(mc->gpio_cp_dump_int);
+
+ sprd8803_get_ops(mc);
+
+ ret = request_irq(mc->irq_phone_active, phone_active_irq_handler,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ "phone_active", mc);
+ if (ret) {
+ pr_err("[MODEM_IF] %s: failed to request_irq:%d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = enable_irq_wake(mc->irq_phone_active);
+ if (ret) {
+ pr_err("[MODEM_IF] %s: failed to enable_irq_wake:%d\n",
+ __func__, ret);
+ free_irq(mc->irq_phone_active, mc);
+ }
+
+ ret = request_irq(irq_cp_dump_int, phone_active_irq_handler,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ "cp_dump_int", mc);
+ if (ret) {
+ pr_err("[MODEM_IF] %s: failed to request_irq:%d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = enable_irq_wake(irq_cp_dump_int);
+ if (ret) {
+ pr_err("[MODEM_IF] %s: failed to enable_irq_wake:%d\n",
+ __func__, ret);
+ free_irq(irq_cp_dump_int, mc);
+ }
+ return ret;
+}
diff --git a/drivers/misc/modem_if/modem_modemctl_device_xmm6262.c b/drivers/misc/modem_if/modem_modemctl_device_xmm6262.c
index 10b0811..4d0b69c 100644
--- a/drivers/misc/modem_if/modem_modemctl_device_xmm6262.c
+++ b/drivers/misc/modem_if/modem_modemctl_device_xmm6262.c
@@ -39,6 +39,12 @@ static int xmm6262_on(struct modem_ctl *mc)
if (mc->gpio_revers_bias_clear)
mc->gpio_revers_bias_clear();
+#ifdef CONFIG_SEC_DUAL_MODEM_MODE
+ gpio_set_value(mc->gpio_sim_io_sel, 0);
+ gpio_set_value(mc->gpio_cp_ctrl1, 1);
+ gpio_set_value(mc->gpio_cp_ctrl2, 0);
+#endif
+
/* TODO */
gpio_set_value(mc->gpio_reset_req_n, 0);
gpio_set_value(mc->gpio_cp_on, 0);
@@ -137,15 +143,19 @@ static irqreturn_t phone_active_irq_handler(int irq, void *_mc)
mif_info("PA EVENT : reset =%d, pa=%d, cp_dump=%d\n",
phone_reset, phone_active_value, cp_dump_value);
- if (phone_reset && phone_active_value)
+ if (phone_reset && phone_active_value) {
phone_state = STATE_BOOTING;
- else if (phone_reset && !phone_active_value) {
- if (cp_dump_value)
- phone_state = STATE_CRASH_EXIT;
- else
- phone_state = STATE_CRASH_RESET;
- } else
+ } else if (mc->dev->power.is_suspended && !phone_active_value) {
+ /*fixing dpm timeout by port2 resume retry*/
+ mif_err("CP reset while dpm resume\n");
+ xmm6262_off(mc);
+ phone_state = STATE_CRASH_RESET;
+ } else if (phone_reset && !phone_active_value) {
+ phone_state =
+ (cp_dump_value) ? STATE_CRASH_EXIT : STATE_CRASH_RESET;
+ } else {
phone_state = STATE_OFFLINE;
+ }
if (mc->iod && mc->iod->modem_state_changed)
mc->iod->modem_state_changed(mc->iod, phone_state);
@@ -168,7 +178,8 @@ static irqreturn_t sim_detect_irq_handler(int irq, void *_mc)
if (mc->iod && mc->iod->sim_state_changed)
mc->iod->sim_state_changed(mc->iod,
- !gpio_get_value(mc->gpio_sim_detect));
+ gpio_get_value(mc->gpio_sim_detect) == mc->sim_polarity
+ );
return IRQ_HANDLED;
}
@@ -196,10 +207,17 @@ int xmm6262_init_modemctl_device(struct modem_ctl *mc,
mc->gpio_flm_uart_sel = pdata->gpio_flm_uart_sel;
mc->gpio_cp_warm_reset = pdata->gpio_cp_warm_reset;
mc->gpio_sim_detect = pdata->gpio_sim_detect;
+ mc->sim_polarity = pdata->sim_polarity;
mc->gpio_revers_bias_clear = pdata->gpio_revers_bias_clear;
mc->gpio_revers_bias_restore = pdata->gpio_revers_bias_restore;
+#ifdef CONFIG_SEC_DUAL_MODEM_MODE
+ mc->gpio_sim_io_sel = pdata->gpio_sim_io_sel;
+ mc->gpio_cp_ctrl1 = pdata->gpio_cp_ctrl1;
+ mc->gpio_cp_ctrl2 = pdata->gpio_cp_ctrl2;
+#endif
+
pdev = to_platform_device(mc->dev);
mc->irq_phone_active = gpio_to_irq(mc->gpio_phone_active);
@@ -241,7 +259,8 @@ int xmm6262_init_modemctl_device(struct modem_ctl *mc,
}
/* initialize sim_state => insert: gpio=0, remove: gpio=1 */
- mc->sim_state.online = !gpio_get_value(mc->gpio_sim_detect);
+ mc->sim_state.online =
+ gpio_get_value(mc->gpio_sim_detect) == mc->sim_polarity;
}
return ret;
diff --git a/drivers/misc/modem_if/modem_prj.h b/drivers/misc/modem_if/modem_prj.h
index 464370f..f85596f 100644
--- a/drivers/misc/modem_if/modem_prj.h
+++ b/drivers/misc/modem_if/modem_prj.h
@@ -23,6 +23,8 @@
#include <linux/wakelock.h>
#include <linux/rbtree.h>
#include <linux/spinlock.h>
+#include <linux/cdev.h>
+#include <linux/types.h>
#define MAX_CPINFO_SIZE 512
@@ -38,7 +40,7 @@
#define IOCTL_MODEM_RESET _IO('o', 0x21)
#define IOCTL_MODEM_BOOT_ON _IO('o', 0x22)
#define IOCTL_MODEM_BOOT_OFF _IO('o', 0x23)
-#define IOCTL_MODEM_START _IO('o', 0x24)
+#define IOCTL_MODEM_BOOT_DONE _IO('o', 0x24)
#define IOCTL_MODEM_PROTOCOL_SUSPEND _IO('o', 0x25)
#define IOCTL_MODEM_PROTOCOL_RESUME _IO('o', 0x26)
@@ -56,6 +58,10 @@
#define IOCTL_MODEM_CP_UPLOAD _IO('o', 0x35)
#define IOCTL_MODEM_DUMP_RESET _IO('o', 0x36)
+#if defined(CONFIG_SEC_DUAL_MODEM_MODE)
+#define IOCTL_MODEM_SWITCH_MODEM _IO('o', 0x37)
+#endif
+
#define IOCTL_DPRAM_SEND_BOOT _IO('o', 0x40)
#define IOCTL_DPRAM_INIT_STATUS _IO('o', 0x43)
@@ -68,6 +74,10 @@
#define IOCTL_DPRAM_PHONE_UPLOAD_STEP1 _IO('o', 0xde)
#define IOCTL_DPRAM_PHONE_UPLOAD_STEP2 _IO('o', 0xdf)
+/* ioctl command for IPC Logger */
+#define IOCTL_MIF_LOG_DUMP _IO('o', 0x51)
+#define IOCTL_MIF_DPRAM_DUMP _IO('o', 0x52)
+
/* modem status */
#define MODEM_OFF 0
#define MODEM_CRASHED 1
@@ -90,6 +100,13 @@
#define SOURCE_MAC_ADDR {0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC}
+/* loopback: CP -> AP -> CP */
+#define CP2AP_LOOPBACK_CHANNEL 30
+
+/* ip loopback */
+#define RMNET0_CH_ID 10
+#define DATA_LOOPBACK_CHANNEL 31
+
/* Debugging features */
#define MAX_MIF_LOG_PATH_LEN 128
#define MAX_MIF_LOG_FILE_SIZE 0x800000 /* 8 MB */
@@ -129,6 +146,7 @@ struct dpram_irq_buff {
unsigned int2cp;
};
+/* Not use */
struct mif_event_buff {
char time[MAX_MIF_TIME_LEN];
@@ -164,6 +182,9 @@ enum modem_state {
STATE_LOADER_DONE,
STATE_SIM_ATTACH,
STATE_SIM_DETACH,
+#if defined(CONFIG_SEC_DUAL_MODEM_MODE)
+ STATE_MODEM_SWITCH,
+#endif
};
enum com_state {
@@ -175,9 +196,9 @@ enum com_state {
};
enum link_mode {
- LINK_MODE_INVALID = 0,
- LINK_MODE_IPC,
+ LINK_MODE_OFFLINE = 0,
LINK_MODE_BOOT,
+ LINK_MODE_IPC,
LINK_MODE_DLOAD,
LINK_MODE_ULOAD,
};
@@ -247,6 +268,8 @@ struct sipc_fmt_hdr {
#define SIPC5_CTL_OFFSET 4
#define SIPC5_CH_ID_RAW_0 0
+#define SIPC5_CH_ID_PDP_0 10
+#define SIPC5_CH_ID_PDP_LAST 24
#define SIPC5_CH_ID_FMT_0 235
#define SIPC5_CH_ID_RFS_0 245
#define SIPC5_CH_ID_MAX 255
@@ -265,7 +288,10 @@ struct sipc5_link_hdr {
u8 cfg;
u8 ch;
u16 len;
- u8 ctl;
+ union {
+ u8 ctl;
+ u16 ext_len;
+ };
} __packed;
struct sipc5_frame_data {
@@ -280,7 +306,6 @@ struct sipc5_frame_data {
/* Frame configuration set by header analysis */
bool padding;
- bool ext_fld;
bool ctl_fld;
bool ext_len;
@@ -303,24 +328,6 @@ struct sipc5_frame_data {
u8 hdr[SIPC5_MAX_HEADER_SIZE];
};
-static inline unsigned sipc5_get_hdr_size(u8 cfg)
-{
- if (cfg & SIPC5_EXT_FIELD_EXIST) {
- if (cfg & SIPC5_CTL_FIELD_EXIST)
- return SIPC5_HEADER_SIZE_WITH_CTL_FLD;
- else
- return SIPC5_HEADER_SIZE_WITH_EXT_LEN;
- } else {
- return SIPC5_MIN_HEADER_SIZE;
- }
-}
-
-static inline unsigned sipc5_calc_padding_size(unsigned len)
-{
- unsigned residue = len & 0x3;
- return residue ? (4 - residue) : 0;
-}
-
struct vnet {
struct io_device *iod;
};
@@ -342,7 +349,9 @@ struct skbuff_private {
struct io_device *iod;
struct link_device *ld;
struct io_device *real_iod; /* for rx multipdp */
-};
+ u8 ch_id;
+ u8 control;
+} __packed;
static inline struct skbuff_private *skbpriv(struct sk_buff *skb)
{
@@ -350,25 +359,6 @@ static inline struct skbuff_private *skbpriv(struct sk_buff *skb)
return (struct skbuff_private *)&skb->cb;
}
-/** rx_alloc_skb - allocate an skbuff and set skb's iod, ld
- * @length: length to allocate
- * @gfp_mask: get_free_pages mask, passed to alloc_skb
- * @iod: struct io_device *
- * @ld: struct link_device *
- *
- * %NULL is returned if there is no free memory.
- */
-static inline struct sk_buff *rx_alloc_skb(unsigned int length,
- gfp_t gfp_mask, struct io_device *iod, struct link_device *ld)
-{
- struct sk_buff *skb = alloc_skb(length, gfp_mask);
- if (likely(skb)) {
- skbpriv(skb)->iod = iod;
- skbpriv(skb)->ld = ld;
- }
- return skb;
-}
-
struct io_device {
/* rb_tree node for an io device */
struct rb_node node_chan;
@@ -398,9 +388,6 @@ struct io_device {
/* SIPC version */
enum sipc_ver ipc_version;
- /* Tx header buffer */
- struct sipc5_frame_data meta_frame;
-
/* Rx queue of sk_buff */
struct sk_buff_head sk_rx_q;
@@ -418,6 +405,8 @@ struct io_device {
/* called from linkdevice when a packet arrives for this iodevice */
int (*recv)(struct io_device *iod, struct link_device *ld,
const char *data, unsigned int len);
+ int (*recv_skb)(struct io_device *iod, struct link_device *ld,
+ struct sk_buff *skb);
/* inform the IO device that the modem is now online or offline or
* crashing or whatever...
@@ -428,6 +417,7 @@ struct io_device {
void (*sim_state_changed)(struct io_device *iod, bool sim_online);
struct modem_ctl *mc;
+ struct modem_shared *msd;
struct wake_lock wakelock;
long waketime;
@@ -463,6 +453,9 @@ struct link_device {
/* Modem control */
struct modem_ctl *mc;
+ /* Modem shared data */
+ struct modem_shared *msd;
+
/* Operation mode of the link device */
enum link_mode mode;
@@ -518,26 +511,68 @@ struct link_device {
unsigned cmd, unsigned long _arg);
};
+/** rx_alloc_skb - allocate an skbuff and set skb's iod, ld
+ * @length: length to allocate
+ * @iod: struct io_device *
+ * @ld: struct link_device *
+ *
+ * %NULL is returned if there is no free memory.
+ */
+static inline struct sk_buff *rx_alloc_skb(unsigned int length,
+ struct io_device *iod, struct link_device *ld)
+{
+ struct sk_buff *skb;
+
+ if (iod->format == IPC_MULTI_RAW || iod->format == IPC_RAW)
+ skb = dev_alloc_skb(length);
+ else
+ skb = alloc_skb(length, GFP_ATOMIC);
+
+ if (likely(skb)) {
+ skbpriv(skb)->iod = iod;
+ skbpriv(skb)->ld = ld;
+ }
+ return skb;
+}
+
struct modemctl_ops {
int (*modem_on) (struct modem_ctl *);
int (*modem_off) (struct modem_ctl *);
int (*modem_reset) (struct modem_ctl *);
int (*modem_boot_on) (struct modem_ctl *);
int (*modem_boot_off) (struct modem_ctl *);
+ int (*modem_boot_done) (struct modem_ctl *);
int (*modem_force_crash_exit) (struct modem_ctl *);
int (*modem_dump_reset) (struct modem_ctl *);
};
-/* mif_common - common data for all io devices and link devices and a modem ctl
- * commons : mc : iod : ld = 1 : 1 : M : N
+/* for IPC Logger */
+struct mif_storage {
+ char *addr;
+ unsigned int cnt;
+};
+
+/* modem_shared - shared data for all io/link devices and a modem ctl
+ * msd : mc : iod : ld = 1 : 1 : M : N
*/
-struct mif_common {
+struct modem_shared {
/* list of link devices */
struct list_head link_dev_list;
/* rb_tree root of io devices. */
struct rb_root iodevs_tree_chan; /* group by channel */
struct rb_root iodevs_tree_fmt; /* group by dev_format */
+
+ /* for IPC Logger */
+ struct mif_storage storage;
+ spinlock_t lock;
+
+ /* loopbacked IP address
+ * default is 0.0.0.0 (disabled)
+ * after you setted this, you can use IP packet loopback using this IP.
+ * exam: echo 1.2.3.4 > /sys/devices/virtual/misc/umts_multipdp/loopback
+ */
+ __be32 loopback_ipaddr;
};
struct modem_ctl {
@@ -545,7 +580,7 @@ struct modem_ctl {
char *name;
struct modem_data *mdm_data;
- struct mif_common commons;
+ struct modem_shared *msd;
enum modem_state phone_state;
struct sim_state sim_state;
@@ -589,46 +624,146 @@ struct modem_ctl {
bool usb_boot;
#endif
+#ifdef CONFIG_TDSCDMA_MODEM_SPRD8803
+ unsigned gpio_ap_cp_int1;
+ unsigned gpio_ap_cp_int2;
+#endif
+
+#ifdef CONFIG_SEC_DUAL_MODEM_MODE
+ unsigned gpio_sim_io_sel;
+ unsigned gpio_cp_ctrl1;
+ unsigned gpio_cp_ctrl2;
+#endif
+
+#ifdef CONFIG_LINK_DEVICE_PLD
+ unsigned gpio_fpga_cs_n;
+#endif
+
struct modemctl_ops ops;
struct io_device *iod;
struct io_device *bootd;
+ /* Wakelock for modem_ctl */
+ struct wake_lock mc_wake_lock;
+
void (*gpio_revers_bias_clear)(void);
void (*gpio_revers_bias_restore)(void);
- /* TODO this will be move to struct mif_common */
- /* For debugging log */
- bool use_mif_log;
- enum mif_event_id log_level;
- atomic_t log_open;
+ bool need_switch_to_usb;
+ bool sim_polarity;
+};
- struct workqueue_struct *evt_wq;
- struct work_struct evt_work;
- struct sk_buff_head evtq;
+int sipc4_init_io_device(struct io_device *iod);
+int sipc5_init_io_device(struct io_device *iod);
- char log_path[MAX_MIF_LOG_PATH_LEN];
- struct file *log_fp;
+/**
+ * sipc5_start_valid
+ * @cfg: configuration field of an SIPC5 link frame
+ *
+ * Returns TRUE if the start (configuration field) of an SIPC5 link frame
+ * is valid or returns FALSE if it is not valid.
+ *
+ */
+static inline int sipc5_start_valid(u8 cfg)
+{
+ return (cfg & SIPC5_START_MASK) == SIPC5_START_MASK;
+}
- bool fs_ready;
- bool fs_failed;
+/**
+ * sipc5_get_hdr_len
+ * @cfg: configuration field of an SIPC5 link frame
+ *
+ * Returns the length of SIPC5 link layer header in an SIPC5 link frame
+ *
+ */
+static inline unsigned sipc5_get_hdr_len(u8 cfg)
+{
+ if (cfg & SIPC5_EXT_FIELD_EXIST) {
+ if (cfg & SIPC5_CTL_FIELD_EXIST)
+ return SIPC5_HEADER_SIZE_WITH_CTL_FLD;
+ else
+ return SIPC5_HEADER_SIZE_WITH_EXT_LEN;
+ } else {
+ return SIPC5_MIN_HEADER_SIZE;
+ }
+}
- char *buff;
-};
-#define to_modem_ctl(mif_common) \
- container_of(mif_common, struct modem_ctl, commons)
+/**
+ * sipc5_get_ch_id
+ * @frm: pointer to an SIPC5 frame
+ *
+ * Returns the channel ID in an SIPC5 link frame
+ *
+ */
+static inline u8 sipc5_get_ch_id(u8 *frm)
+{
+ return *(frm + SIPC5_CH_ID_OFFSET);
+}
-int sipc4_init_io_device(struct io_device *iod);
-int sipc5_init_io_device(struct io_device *iod);
+/**
+ * sipc5_get_frame_sz16
+ * @frm: pointer to an SIPC5 link frame
+ *
+ * Returns the length of an SIPC5 link frame without the extended length field
+ *
+ */
+static inline unsigned sipc5_get_frame_sz16(u8 *frm)
+{
+ return *((u16 *)(frm + SIPC5_LEN_OFFSET));
+}
+
+/**
+ * sipc5_get_frame_sz32
+ * @frm: pointer to an SIPC5 frame
+ *
+ * Returns the length of an SIPC5 link frame with the extended length field
+ *
+ */
+static inline unsigned sipc5_get_frame_sz32(u8 *frm)
+{
+ return *((u32 *)(frm + SIPC5_LEN_OFFSET));
+}
+
+/**
+ * sipc5_calc_padding_size
+ * @len: length of an SIPC5 link frame
+ *
+ * Returns the padding size for an SIPC5 link frame
+ *
+ */
+static inline unsigned sipc5_calc_padding_size(unsigned len)
+{
+ unsigned residue = len & 0x3;
+ return residue ? (4 - residue) : 0;
+}
-int mif_init_log(struct modem_ctl *mc);
-void mif_set_log_level(struct modem_ctl *mc);
-int mif_open_log_file(struct modem_ctl *mc);
-void mif_close_log_file(struct modem_ctl *mc);
+extern void set_sromc_access(bool access);
-void mif_irq_log(struct modem_ctl *mc, struct sk_buff *skb);
-void mif_ipc_log(struct modem_ctl *mc, enum mif_event_id evt,
- struct io_device *iod, struct link_device *ld,
- u8 *data, unsigned size);
-void mif_flush_logs(struct modem_ctl *mc);
+#if defined(CONFIG_TDSCDMA_MODEM_SPRD8803) && defined(CONFIG_LINK_DEVICE_SPI)
+extern int spi_sema_init(void);
+extern int sprd_boot_done;
+struct ipc_spi {
+ struct class *class;
+ struct device *dev;
+ struct cdev cdev;
+ dev_t devid;
+
+ wait_queue_head_t waitq;
+ struct fasync_struct *async_queue;
+ u32 mailbox;
+
+ unsigned long base;
+ unsigned long size;
+ void __iomem *mmio;
+
+ int irq;
+
+ struct completion comp;
+ atomic_t ref_sem;
+ unsigned long flags;
+
+ const struct attribute_group *group;
+};
+#endif
#endif
diff --git a/drivers/misc/modem_if/modem_sim_slot_switch.c b/drivers/misc/modem_if/modem_sim_slot_switch.c
new file mode 100644
index 0000000..1dd4c67
--- /dev/null
+++ b/drivers/misc/modem_if/modem_sim_slot_switch.c
@@ -0,0 +1,92 @@
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+
+#include <plat/gpio-cfg.h>
+
+#include <mach/gpio.h>
+
+extern struct class *sec_class;
+struct device *slot_switch_dev;
+
+static ssize_t get_slot_switch(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ int value;
+
+ //return '0' slot path is '||', return '1' slot path is 'X'
+ value = gpio_get_value(GPIO_UIM_SIM_SEL);
+ printk("Current Slot is %x\n", value);
+
+ return sprintf(buf, "%d\n", value);
+}
+
+static ssize_t set_slot_switch(struct device *dev, struct device_attribute *attr, const char *buf, size_t size)
+{
+ int value;
+
+ sscanf(buf, "%d", &value);
+
+ switch(value) {
+ case 0:
+ gpio_set_value(GPIO_UIM_SIM_SEL, 0);
+ printk("set slot switch to %x\n", gpio_get_value(GPIO_UIM_SIM_SEL));
+ break;
+ case 1:
+ gpio_set_value(GPIO_UIM_SIM_SEL, 1);
+ printk("set slot switch to %x\n", gpio_get_value(GPIO_UIM_SIM_SEL));
+ break;
+ default:
+ printk("Enter 0 or 1!!\n");
+ }
+
+ return size;
+}
+
+static DEVICE_ATTR(slot_sel, S_IRUGO |S_IWUGO | S_IRUSR | S_IWUSR, get_slot_switch, set_slot_switch);
+
+static int __init slot_switch_manager_init(void)
+{
+ int ret = 0;
+ int err = 0;
+
+ printk("slot_switch_manager_init\n");
+
+ //initailize uim_sim_switch gpio
+ err = gpio_request(GPIO_UIM_SIM_SEL, "PDA_ACTIVE");
+ if (err) {
+ pr_err("fail to request gpio %s, gpio %d, errno %d\n",
+ "PDA_ACTIVE", GPIO_UIM_SIM_SEL, err);
+ } else {
+ gpio_direction_output(GPIO_UIM_SIM_SEL, 1);
+ s3c_gpio_setpull(GPIO_UIM_SIM_SEL, S3C_GPIO_PULL_NONE);
+#if defined(CONFIG_MACH_T0_CHN_CTC)
+ gpio_set_value(GPIO_UIM_SIM_SEL, 1);
+#else
+ gpio_set_value(GPIO_UIM_SIM_SEL, 0);
+#endif
+ }
+
+ //initailize slot switch device
+ slot_switch_dev = device_create(sec_class,
+ NULL, 0, NULL, "slot_switch");
+ if (IS_ERR(slot_switch_dev))
+ pr_err("Failed to create device(switch)!\n");
+
+ if (device_create_file(slot_switch_dev, &dev_attr_slot_sel) < 0)
+ pr_err("Failed to create device file(%s)!\n",
+ dev_attr_slot_sel.attr.name);
+
+ return ret;
+}
+
+static void __exit slot_switch_manager_exit(void)
+{
+}
+
+module_init(slot_switch_manager_init);
+module_exit(slot_switch_manager_exit);
+
+MODULE_AUTHOR("SAMSUNG ELECTRONICS CO., LTD");
+MODULE_DESCRIPTION("Slot Switch");
+MODULE_LICENSE("GPL");
diff --git a/drivers/misc/modem_if/modem_utils.c b/drivers/misc/modem_if/modem_utils.c
index 24a9d19..d62aaa6 100644
--- a/drivers/misc/modem_if/modem_utils.c
+++ b/drivers/misc/modem_if/modem_utils.c
@@ -19,6 +19,8 @@
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/udp.h>
+#include <linux/rtc.h>
+#include <linux/time.h>
#include <net/ip.h>
#include "modem_prj.h"
@@ -27,6 +29,183 @@
#define CMD_SUSPEND ((unsigned short)(0x00CA))
#define CMD_RESUME ((unsigned short)(0x00CB))
+#define TX_SEPARATOR "mif: >>>>>>>>>> Outgoing packet "
+#define RX_SEPARATOR "mif: Incoming packet <<<<<<<<<<"
+#define LINE_SEPARATOR \
+ "mif: ------------------------------------------------------------"
+#define LINE_BUFF_SIZE 80
+
+#ifdef CONFIG_LINK_DEVICE_DPRAM
+#include "modem_link_device_dpram.h"
+int mif_dump_dpram(struct io_device *iod)
+{
+ struct link_device *ld = get_current_link(iod);
+ struct dpram_link_device *dpld = to_dpram_link_device(ld);
+ u32 size = dpld->dp_size;
+ unsigned long read_len = 0;
+ struct sk_buff *skb;
+ char *buff;
+
+ buff = kzalloc(size, GFP_ATOMIC);
+ if (!buff) {
+ pr_err("[MIF] <%s> alloc dpram buff failed\n", __func__);
+ return -ENOMEM;
+ } else {
+ dpld->dpram_dump(ld, buff);
+ }
+
+ while (read_len < size) {
+ skb = alloc_skb(MAX_IPC_SKB_SIZE, GFP_ATOMIC);
+ if (!skb) {
+ pr_err("[MIF] <%s> alloc skb failed\n", __func__);
+ kfree(buff);
+ return -ENOMEM;
+ }
+ memcpy(skb_put(skb, MAX_IPC_SKB_SIZE),
+ buff + read_len, MAX_IPC_SKB_SIZE);
+ skb_queue_tail(&iod->sk_rx_q, skb);
+ read_len += MAX_IPC_SKB_SIZE;
+ wake_up(&iod->wq);
+ }
+ kfree(buff);
+ return 0;
+}
+#endif
+
+int mif_dump_log(struct modem_shared *msd, struct io_device *iod)
+{
+ struct sk_buff *skb;
+ unsigned long read_len = 0;
+ unsigned long int flags;
+
+ spin_lock_irqsave(&msd->lock, flags);
+ while (read_len < MAX_MIF_BUFF_SIZE) {
+ skb = alloc_skb(MAX_IPC_SKB_SIZE, GFP_ATOMIC);
+ if (!skb) {
+ pr_err("[MIF] <%s> alloc skb failed\n", __func__);
+ spin_unlock_irqrestore(&msd->lock, flags);
+ return -ENOMEM;
+ }
+ memcpy(skb_put(skb, MAX_IPC_SKB_SIZE),
+ msd->storage.addr + read_len, MAX_IPC_SKB_SIZE);
+ skb_queue_tail(&iod->sk_rx_q, skb);
+ read_len += MAX_IPC_SKB_SIZE;
+ wake_up(&iod->wq);
+ }
+ spin_unlock_irqrestore(&msd->lock, flags);
+ return 0;
+}
+
+static unsigned long long get_kernel_time(void)
+{
+ int this_cpu;
+ unsigned long flags;
+ unsigned long long time;
+
+ preempt_disable();
+ raw_local_irq_save(flags);
+
+ this_cpu = smp_processor_id();
+ time = cpu_clock(this_cpu);
+
+ preempt_enable();
+ raw_local_irq_restore(flags);
+
+ return time;
+}
+
+void mif_ipc_log(enum mif_log_id id,
+ struct modem_shared *msd, const char *data, size_t len)
+{
+ struct mif_ipc_block *block;
+ unsigned long int flags;
+
+ spin_lock_irqsave(&msd->lock, flags);
+
+ block = (struct mif_ipc_block *)
+ (msd->storage.addr + (MAX_LOG_SIZE * msd->storage.cnt));
+ msd->storage.cnt = ((msd->storage.cnt + 1) < MAX_LOG_CNT) ?
+ msd->storage.cnt + 1 : 0;
+
+ spin_unlock_irqrestore(&msd->lock, flags);
+
+ block->id = id;
+ block->time = get_kernel_time();
+ block->len = (len > MAX_IPC_LOG_SIZE) ? MAX_IPC_LOG_SIZE : len;
+ memcpy(block->buff, data, block->len);
+}
+
+void _mif_irq_log(enum mif_log_id id, struct modem_shared *msd,
+ struct mif_irq_map map, const char *data, size_t len)
+{
+ struct mif_irq_block *block;
+ unsigned long int flags;
+
+ spin_lock_irqsave(&msd->lock, flags);
+
+ block = (struct mif_irq_block *)
+ (msd->storage.addr + (MAX_LOG_SIZE * msd->storage.cnt));
+ msd->storage.cnt = ((msd->storage.cnt + 1) < MAX_LOG_CNT) ?
+ msd->storage.cnt + 1 : 0;
+
+ spin_unlock_irqrestore(&msd->lock, flags);
+
+ block->id = id;
+ block->time = get_kernel_time();
+ memcpy(&(block->map), &map, sizeof(struct mif_irq_map));
+ if (data)
+ memcpy(block->buff, data,
+ (len > MAX_IRQ_LOG_SIZE) ? MAX_IRQ_LOG_SIZE : len);
+}
+
+void _mif_com_log(enum mif_log_id id,
+ struct modem_shared *msd, const char *format, ...)
+{
+ struct mif_common_block *block;
+ unsigned long int flags;
+ va_list args;
+ int ret;
+
+ spin_lock_irqsave(&msd->lock, flags);
+
+ block = (struct mif_common_block *)
+ (msd->storage.addr + (MAX_LOG_SIZE * msd->storage.cnt));
+ msd->storage.cnt = ((msd->storage.cnt + 1) < MAX_LOG_CNT) ?
+ msd->storage.cnt + 1 : 0;
+
+ spin_unlock_irqrestore(&msd->lock, flags);
+
+ block->id = id;
+ block->time = get_kernel_time();
+
+ va_start(args, format);
+ ret = vsnprintf(block->buff, MAX_COM_LOG_SIZE, format, args);
+ va_end(args);
+}
+
+void _mif_time_log(enum mif_log_id id, struct modem_shared *msd,
+ struct timespec epoch, const char *data, size_t len)
+{
+ struct mif_time_block *block;
+ unsigned long int flags;
+
+ spin_lock_irqsave(&msd->lock, flags);
+
+ block = (struct mif_time_block *)
+ (msd->storage.addr + (MAX_LOG_SIZE * msd->storage.cnt));
+ msd->storage.cnt = ((msd->storage.cnt + 1) < MAX_LOG_CNT) ?
+ msd->storage.cnt + 1 : 0;
+
+ spin_unlock_irqrestore(&msd->lock, flags);
+
+ block->id = id;
+ block->time = get_kernel_time();
+ memcpy(&block->epoch, &epoch, sizeof(struct timespec));
+
+ if (data)
+ memcpy(block->buff, data,
+ (len > MAX_IRQ_LOG_SIZE) ? MAX_IRQ_LOG_SIZE : len);
+}
/* dump2hex
* dump data to hex as fast as possible.
@@ -52,6 +231,23 @@ static inline int dump2hex(char *buf, const char *data, size_t len)
return dest - buf;
}
+int pr_ipc(const char *str, const char *data, size_t len)
+{
+ struct timeval tv;
+ struct tm date;
+ unsigned char hexstr[128];
+
+ do_gettimeofday(&tv);
+ time_to_tm((tv.tv_sec - sys_tz.tz_minuteswest * 60), 0, &date);
+
+ dump2hex(hexstr, data, (len > 40 ? 40 : len));
+
+ return pr_info("mif: %s: [%02d-%02d %02d:%02d:%02d.%03ld] %s\n",
+ str, date.tm_mon + 1, date.tm_mday,
+ date.tm_hour, date.tm_min, date.tm_sec,
+ (tv.tv_usec > 0 ? tv.tv_usec / 1000 : 0), hexstr);
+}
+
/* print buffer as hex string */
int pr_buffer(const char *tag, const char *data, size_t data_len,
size_t max_len)
@@ -61,14 +257,14 @@ int pr_buffer(const char *tag, const char *data, size_t data_len,
dump2hex(hexstr, data, len);
/* don't change this printk to mif_debug for print this as level7 */
- return printk(KERN_DEBUG "%s(%u): %s%s\n", tag, data_len, hexstr,
+ return printk(KERN_INFO "mif: %s(%u): %s%s\n", tag, data_len, hexstr,
len == data_len ? "" : " ...");
}
-/* flow control CMfrom CP, it use in serial devices */
+/* flow control CM from CP, it use in serial devices */
int link_rx_flowctl_cmd(struct link_device *ld, const char *data, size_t len)
{
- struct mif_common *commons = &ld->mc->commons;
+ struct modem_shared *msd = ld->msd;
unsigned short *cmd, *end = (unsigned short *)(data + len);
mif_debug("flow control cmd: size=%d\n", len);
@@ -76,13 +272,13 @@ int link_rx_flowctl_cmd(struct link_device *ld, const char *data, size_t len)
for (cmd = (unsigned short *)data; cmd < end; cmd++) {
switch (*cmd) {
case CMD_SUSPEND:
- iodevs_for_each(commons, iodev_netif_stop, 0);
+ iodevs_for_each(msd, iodev_netif_stop, 0);
ld->raw_tx_suspended = true;
mif_info("flowctl CMD_SUSPEND(%04X)\n", *cmd);
break;
case CMD_RESUME:
- iodevs_for_each(commons, iodev_netif_wake, 0);
+ iodevs_for_each(msd, iodev_netif_wake, 0);
ld->raw_tx_suspended = false;
complete_all(&ld->raw_tx_resumed_by_cp);
mif_info("flowctl CMD_RESUME(%04X)\n", *cmd);
@@ -97,10 +293,10 @@ int link_rx_flowctl_cmd(struct link_device *ld, const char *data, size_t len)
return 0;
}
-struct io_device *get_iod_with_channel(struct mif_common *commons,
+struct io_device *get_iod_with_channel(struct modem_shared *msd,
unsigned channel)
{
- struct rb_node *n = commons->iodevs_tree_chan.rb_node;
+ struct rb_node *n = msd->iodevs_tree_chan.rb_node;
struct io_device *iodev;
while (n) {
iodev = rb_entry(n, struct io_device, node_chan);
@@ -114,10 +310,10 @@ struct io_device *get_iod_with_channel(struct mif_common *commons,
return NULL;
}
-struct io_device *get_iod_with_format(struct mif_common *commons,
+struct io_device *get_iod_with_format(struct modem_shared *msd,
enum dev_format format)
{
- struct rb_node *n = commons->iodevs_tree_fmt.rb_node;
+ struct rb_node *n = msd->iodevs_tree_fmt.rb_node;
struct io_device *iodev;
while (n) {
iodev = rb_entry(n, struct io_device, node_fmt);
@@ -131,10 +327,10 @@ struct io_device *get_iod_with_format(struct mif_common *commons,
return NULL;
}
-struct io_device *insert_iod_with_channel(struct mif_common *commons,
+struct io_device *insert_iod_with_channel(struct modem_shared *msd,
unsigned channel, struct io_device *iod)
{
- struct rb_node **p = &commons->iodevs_tree_chan.rb_node;
+ struct rb_node **p = &msd->iodevs_tree_chan.rb_node;
struct rb_node *parent = NULL;
struct io_device *iodev;
while (*p) {
@@ -148,14 +344,14 @@ struct io_device *insert_iod_with_channel(struct mif_common *commons,
return iodev;
}
rb_link_node(&iod->node_chan, parent, p);
- rb_insert_color(&iod->node_chan, &commons->iodevs_tree_chan);
+ rb_insert_color(&iod->node_chan, &msd->iodevs_tree_chan);
return NULL;
}
-struct io_device *insert_iod_with_format(struct mif_common *commons,
+struct io_device *insert_iod_with_format(struct modem_shared *msd,
enum dev_format format, struct io_device *iod)
{
- struct rb_node **p = &commons->iodevs_tree_fmt.rb_node;
+ struct rb_node **p = &msd->iodevs_tree_fmt.rb_node;
struct rb_node *parent = NULL;
struct io_device *iodev;
while (*p) {
@@ -169,14 +365,14 @@ struct io_device *insert_iod_with_format(struct mif_common *commons,
return iodev;
}
rb_link_node(&iod->node_fmt, parent, p);
- rb_insert_color(&iod->node_fmt, &commons->iodevs_tree_fmt);
+ rb_insert_color(&iod->node_fmt, &msd->iodevs_tree_fmt);
return NULL;
}
-void iodevs_for_each(struct mif_common *commons, action_fn action, void *args)
+void iodevs_for_each(struct modem_shared *msd, action_fn action, void *args)
{
struct io_device *iod;
- struct rb_node *node = rb_first(&commons->iodevs_tree_chan);
+ struct rb_node *node = rb_first(&msd->iodevs_tree_chan);
for (; node; node = rb_next(node)) {
iod = rb_entry(node, struct io_device, node_chan);
action(iod, args);
@@ -202,22 +398,48 @@ void iodev_netif_stop(struct io_device *iod, void *args)
static void iodev_set_tx_link(struct io_device *iod, void *args)
{
struct link_device *ld = (struct link_device *)args;
- if (iod->io_typ == IODEV_NET && IS_CONNECTED(iod, ld)) {
+ if (iod->format == IPC_RAW && IS_CONNECTED(iod, ld)) {
set_current_link(iod, ld);
mif_err("%s -> %s\n", iod->name, ld->name);
}
}
-void rawdevs_set_tx_link(struct mif_common *commons, enum modem_link link_type)
+void rawdevs_set_tx_link(struct modem_shared *msd, enum modem_link link_type)
{
- struct link_device *ld = find_linkdev(commons, link_type);
+ struct link_device *ld = find_linkdev(msd, link_type);
if (ld)
- iodevs_for_each(commons, iodev_set_tx_link, ld);
+ iodevs_for_each(msd, iodev_set_tx_link, ld);
+}
+
+/**
+ * ipv4str_to_be32 - ipv4 string to be32 (big endian 32bits integer)
+ * @return: return zero when errors occurred
+ */
+__be32 ipv4str_to_be32(const char *ipv4str, size_t count)
+{
+ unsigned char ip[4];
+ char ipstr[16]; /* == strlen("xxx.xxx.xxx.xxx") + 1 */
+ char *next = ipstr;
+ char *p;
+ int i;
+
+ strncpy(ipstr, ipv4str, ARRAY_SIZE(ipstr));
+
+ for (i = 0; i < 4; i++) {
+ p = strsep(&next, ".");
+ if (kstrtou8(p, 10, &ip[i]) < 0)
+ return 0; /* == 0.0.0.0 */
+ }
+
+ return *((__be32 *)ip);
}
void mif_add_timer(struct timer_list *timer, unsigned long expire,
void (*function)(unsigned long), unsigned long data)
{
+ if (timer_pending(timer))
+ return;
+
init_timer(timer);
timer->expires = get_jiffies_64() + expire;
timer->function = function;
@@ -375,13 +597,12 @@ void print_sipc5_link_fmt_frame(const u8 *psrc)
mif_err("-----------------------------------------------------------\n");
}
-static void print_tcp_header(u8 *pkt)
+static void strcat_tcp_header(char *buff, u8 *pkt)
{
- int i;
- char tcp_flags[32];
struct tcphdr *tcph = (struct tcphdr *)pkt;
- u8 *opt = pkt + TCP_HDR_SIZE;
- unsigned opt_len = (tcph->doff << 2) - TCP_HDR_SIZE;
+ int eol;
+ char line[LINE_BUFF_SIZE];
+ char flag_str[32];
/*-------------------------------------------------------------------------
@@ -409,44 +630,54 @@ static void print_tcp_header(u8 *pkt)
-------------------------------------------------------------------------*/
- memset(tcp_flags, 0, sizeof(tcp_flags));
+ memset(line, 0, LINE_BUFF_SIZE);
+ snprintf(line, LINE_BUFF_SIZE,
+ "mif: TCP:: Src.Port %u, Dst.Port %u\n",
+ ntohs(tcph->source), ntohs(tcph->dest));
+ strcat(buff, line);
+
+ memset(line, 0, LINE_BUFF_SIZE);
+ snprintf(line, LINE_BUFF_SIZE,
+ "mif: TCP:: SEQ 0x%08X(%u), ACK 0x%08X(%u)\n",
+ ntohs(tcph->seq), ntohs(tcph->seq),
+ ntohs(tcph->ack_seq), ntohs(tcph->ack_seq));
+ strcat(buff, line);
+
+ memset(line, 0, LINE_BUFF_SIZE);
+ memset(flag_str, 0, sizeof(flag_str));
if (tcph->cwr)
- strcat(tcp_flags, "CWR ");
+ strcat(flag_str, "CWR ");
if (tcph->ece)
- strcat(tcp_flags, "EC");
+ strcat(flag_str, "ECE");
if (tcph->urg)
- strcat(tcp_flags, "URG ");
+ strcat(flag_str, "URG ");
if (tcph->ack)
- strcat(tcp_flags, "ACK ");
+ strcat(flag_str, "ACK ");
if (tcph->psh)
- strcat(tcp_flags, "PSH ");
+ strcat(flag_str, "PSH ");
if (tcph->rst)
- strcat(tcp_flags, "RST ");
+ strcat(flag_str, "RST ");
if (tcph->syn)
- strcat(tcp_flags, "SYN ");
+ strcat(flag_str, "SYN ");
if (tcph->fin)
- strcat(tcp_flags, "FIN ");
-
- mif_err("TCP:: Src.Port %u, Dst.Port %u\n",
- ntohs(tcph->source), ntohs(tcph->dest));
- mif_err("TCP:: SEQ 0x%08X(%u), ACK 0x%08X(%u)\n",
- ntohs(tcph->seq), ntohs(tcph->seq),
- ntohs(tcph->ack_seq), ntohs(tcph->ack_seq));
- mif_err("TCP:: Flags {%s}\n", tcp_flags);
- mif_err("TCP:: Window %u, Checksum 0x%04X, Urg Pointer %u\n",
+ strcat(flag_str, "FIN ");
+ eol = strlen(flag_str) - 1;
+ if (eol > 0)
+ flag_str[eol] = 0;
+ snprintf(line, LINE_BUFF_SIZE, "mif: TCP:: Flags {%s}\n", flag_str);
+ strcat(buff, line);
+
+ memset(line, 0, LINE_BUFF_SIZE);
+ snprintf(line, LINE_BUFF_SIZE,
+ "mif: TCP:: Window %u, Checksum 0x%04X, Urg Pointer %u\n",
ntohs(tcph->window), ntohs(tcph->check), ntohs(tcph->urg_ptr));
-
- if (opt_len > 0) {
- mif_err("TCP:: Options {");
- for (i = 0; i < opt_len; i++)
- mif_err("%02X ", opt[i]);
- mif_err("}\n");
- }
+ strcat(buff, line);
}
-static void print_udp_header(u8 *pkt)
+static void strcat_udp_header(char *buff, u8 *pkt)
{
struct udphdr *udph = (struct udphdr *)pkt;
+ char line[LINE_BUFF_SIZE];
/*-------------------------------------------------------------------------
@@ -462,30 +693,43 @@ static void print_udp_header(u8 *pkt)
-------------------------------------------------------------------------*/
- mif_err("UDP:: Src.Port %u, Dst.Port %u\n",
+ memset(line, 0, LINE_BUFF_SIZE);
+ snprintf(line, LINE_BUFF_SIZE,
+ "mif: UDP:: Src.Port %u, Dst.Port %u\n",
ntohs(udph->source), ntohs(udph->dest));
- mif_err("UDP:: Length %u, Checksum 0x%04X\n",
+ strcat(buff, line);
+
+ memset(line, 0, LINE_BUFF_SIZE);
+ snprintf(line, LINE_BUFF_SIZE,
+ "mif: UDP:: Length %u, Checksum 0x%04X\n",
ntohs(udph->len), ntohs(udph->check));
+ strcat(buff, line);
- if (ntohs(udph->dest) == 53)
- mif_err("UDP:: DNS query!!!\n");
+ if (ntohs(udph->dest) == 53) {
+ memset(line, 0, LINE_BUFF_SIZE);
+ snprintf(line, LINE_BUFF_SIZE, "mif: UDP:: DNS query!!!\n");
+ strcat(buff, line);
+ }
- if (ntohs(udph->source) == 53)
- mif_err("UDP:: DNS response!!!\n");
+ if (ntohs(udph->source) == 53) {
+ memset(line, 0, LINE_BUFF_SIZE);
+ snprintf(line, LINE_BUFF_SIZE, "mif: UDP:: DNS response!!!\n");
+ strcat(buff, line);
+ }
}
-void print_ip4_packet(u8 *ip_pkt)
+void print_ip4_packet(u8 *ip_pkt, bool tx)
{
- char ip_flags[16];
+ char *buff;
struct iphdr *iph = (struct iphdr *)ip_pkt;
u8 *pkt = ip_pkt + (iph->ihl << 2);
u16 flags = (ntohs(iph->frag_off) & 0xE000);
u16 frag_off = (ntohs(iph->frag_off) & 0x1FFF);
-
- mif_err("-----------------------------------------------------------\n");
+ int eol;
+ char line[LINE_BUFF_SIZE];
+ char flag_str[16];
/*---------------------------------------------------------------------------
-
IPv4 Header Format
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
@@ -513,40 +757,234 @@ void print_ip4_packet(u8 *ip_pkt)
---------------------------------------------------------------------------*/
- memset(ip_flags, 0, sizeof(ip_flags));
- if (flags & IP_CE)
- strcat(ip_flags, "C");
- if (flags & IP_DF)
- strcat(ip_flags, "D");
- if (flags & IP_MF)
- strcat(ip_flags, "M");
+ if (iph->version != 4)
+ return;
- mif_err("IP4:: Version %u, Header Length %u, TOS %u, Length %u\n",
+ buff = kzalloc(4096, GFP_ATOMIC);
+ if (!buff)
+ return;
+
+
+ memset(line, 0, LINE_BUFF_SIZE);
+ if (tx)
+ snprintf(line, LINE_BUFF_SIZE, "\n%s\n", TX_SEPARATOR);
+ else
+ snprintf(line, LINE_BUFF_SIZE, "\n%s\n", RX_SEPARATOR);
+ strcat(buff, line);
+
+ memset(line, 0, LINE_BUFF_SIZE);
+ snprintf(line, LINE_BUFF_SIZE, "%s\n", LINE_SEPARATOR);
+ strcat(buff, line);
+
+ memset(line, 0, LINE_BUFF_SIZE);
+ snprintf(line, LINE_BUFF_SIZE,
+ "mif: IP4:: Version %u, Header Length %u, TOS %u, Length %u\n",
iph->version, (iph->ihl << 2), iph->tos, ntohs(iph->tot_len));
- mif_err("IP4:: I%u, Fragment Offset %u\n",
+ strcat(buff, line);
+
+ memset(line, 0, LINE_BUFF_SIZE);
+ snprintf(line, LINE_BUFF_SIZE,
+ "mif: IP4:: ID %u, Fragment Offset %u\n",
ntohs(iph->id), frag_off);
- mif_err("IP4:: Flags {%s}\n", ip_flags);
- mif_err("IP4:: TTL %u, Protocol %u, Header Checksum 0x%04X\n",
+ strcat(buff, line);
+
+ memset(line, 0, LINE_BUFF_SIZE);
+ memset(flag_str, 0, sizeof(flag_str));
+ if (flags & IP_CE)
+ strcat(flag_str, "CE ");
+ if (flags & IP_DF)
+ strcat(flag_str, "DF ");
+ if (flags & IP_MF)
+ strcat(flag_str, "MF ");
+ eol = strlen(flag_str) - 1;
+ if (eol > 0)
+ flag_str[eol] = 0;
+ snprintf(line, LINE_BUFF_SIZE, "mif: IP4:: Flags {%s}\n", flag_str);
+ strcat(buff, line);
+
+ memset(line, 0, LINE_BUFF_SIZE);
+ snprintf(line, LINE_BUFF_SIZE,
+ "mif: IP4:: TTL %u, Protocol %u, Header Checksum 0x%04X\n",
iph->ttl, iph->protocol, ntohs(iph->check));
- mif_err("IP4:: Src.IP %u.%u.%u.%u, Dst.IP %u.%u.%u.%u\n",
+ strcat(buff, line);
+
+ memset(line, 0, LINE_BUFF_SIZE);
+ snprintf(line, LINE_BUFF_SIZE,
+ "mif: IP4:: Src.IP %u.%u.%u.%u, Dst.IP %u.%u.%u.%u\n",
ip_pkt[12], ip_pkt[13], ip_pkt[14], ip_pkt[15],
ip_pkt[16], ip_pkt[17], ip_pkt[18], ip_pkt[19]);
+ strcat(buff, line);
switch (iph->protocol) {
- case 6:
- /* TCP */
- print_tcp_header(pkt);
+ case 6: /* TCP */
+ strcat_tcp_header(buff, pkt);
break;
- case 17:
- /* UDP */
- print_udp_header(pkt);
+ case 17: /* UDP */
+ strcat_udp_header(buff, pkt);
break;
default:
break;
}
- mif_err("-----------------------------------------------------------\n");
+ memset(line, 0, LINE_BUFF_SIZE);
+ snprintf(line, LINE_BUFF_SIZE, "%s\n", LINE_SEPARATOR);
+ strcat(buff, line);
+
+ pr_info("%s", buff);
+
+ kfree(buff);
+}
+
+bool is_dns_packet(u8 *ip_pkt)
+{
+ struct iphdr *iph = (struct iphdr *)ip_pkt;
+ struct udphdr *udph = (struct udphdr *)(ip_pkt + (iph->ihl << 2));
+
+ /* If this packet is not a UDP packet, return here. */
+ if (iph->protocol != 17)
+ return false;
+
+ if (ntohs(udph->dest) == 53 || ntohs(udph->source) == 53)
+ return true;
+ else
+ return false;
+}
+
+bool is_syn_packet(u8 *ip_pkt)
+{
+ struct iphdr *iph = (struct iphdr *)ip_pkt;
+ struct tcphdr *tcph = (struct tcphdr *)(ip_pkt + (iph->ihl << 2));
+
+ /* If this packet is not a TCP packet, return here. */
+ if (iph->protocol != 6)
+ return false;
+
+ if (tcph->syn || tcph->fin)
+ return true;
+ else
+ return false;
+}
+
+int memcmp16_to_io(const void __iomem *to, void *from, int size)
+{
+ u16 *d = (u16 *)to;
+ u16 *s = (u16 *)from;
+ int count = size >> 1;
+ int diff = 0;
+ int i;
+ u16 d1;
+ u16 s1;
+
+ for (i = 0; i < count; i++) {
+ d1 = ioread16(d);
+ s1 = *s;
+ if (d1 != s1) {
+ diff++;
+ mif_err("ERR! [%d] d:0x%04X != s:0x%04X\n", i, d1, s1);
+ }
+ d++;
+ s++;
+ }
+
+ return diff;
+}
+
+int mif_test_dpram(char *dp_name, u8 __iomem *start, u32 size)
+{
+ u8 __iomem *dst;
+ int i;
+ u16 val;
+
+ mif_info("%s: start = 0x%p, size = %d\n", dp_name, start, size);
+
+ dst = start;
+ for (i = 0; i < (size >> 1); i++) {
+ iowrite16((i & 0xFFFF), dst);
+ dst += 2;
+ }
+
+ dst = start;
+ for (i = 0; i < (size >> 1); i++) {
+ val = ioread16(dst);
+ if (val != (i & 0xFFFF)) {
+ mif_info("%s: ERR! dst[%d] 0x%04X != 0x%04X\n",
+ dp_name, i, val, (i & 0xFFFF));
+ return -EINVAL;
+ }
+ dst += 2;
+ }
+
+ dst = start;
+ for (i = 0; i < (size >> 1); i++) {
+ iowrite16(0x00FF, dst);
+ dst += 2;
+ }
+
+ dst = start;
+ for (i = 0; i < (size >> 1); i++) {
+ val = ioread16(dst);
+ if (val != 0x00FF) {
+ mif_info("%s: ERR! dst[%d] 0x%04X != 0x00FF\n",
+ dp_name, i, val);
+ return -EINVAL;
+ }
+ dst += 2;
+ }
+
+ dst = start;
+ for (i = 0; i < (size >> 1); i++) {
+ iowrite16(0x0FF0, dst);
+ dst += 2;
+ }
+
+ dst = start;
+ for (i = 0; i < (size >> 1); i++) {
+ val = ioread16(dst);
+ if (val != 0x0FF0) {
+ mif_info("%s: ERR! dst[%d] 0x%04X != 0x0FF0\n",
+ dp_name, i, val);
+ return -EINVAL;
+ }
+ dst += 2;
+ }
+
+ dst = start;
+ for (i = 0; i < (size >> 1); i++) {
+ iowrite16(0xFF00, dst);
+ dst += 2;
+ }
+
+ dst = start;
+ for (i = 0; i < (size >> 1); i++) {
+ val = ioread16(dst);
+ if (val != 0xFF00) {
+ mif_info("%s: ERR! dst[%d] 0x%04X != 0xFF00\n",
+ dp_name, i, val);
+ return -EINVAL;
+ }
+ dst += 2;
+ }
+
+ dst = start;
+ for (i = 0; i < (size >> 1); i++) {
+ iowrite16(0, dst);
+ dst += 2;
+ }
+
+ dst = start;
+ for (i = 0; i < (size >> 1); i++) {
+ val = ioread16(dst);
+ if (val != 0) {
+ mif_info("%s: ERR! dst[%d] 0x%04X != 0\n",
+ dp_name, i, val);
+ return -EINVAL;
+ }
+ dst += 2;
+ }
+
+ mif_info("%s: PASS!!!\n", dp_name);
+ return 0;
}
diff --git a/drivers/misc/modem_if/modem_utils.h b/drivers/misc/modem_if/modem_utils.h
index 60e4820..7225ec2 100644
--- a/drivers/misc/modem_if/modem_utils.h
+++ b/drivers/misc/modem_if/modem_utils.h
@@ -19,14 +19,110 @@
#define IS_CONNECTED(iod, ld) ((iod)->link_types & LINKTYPE((ld)->link_type))
+#define MAX_MIF_BUFF_SIZE 0x80000 /* 512kb */
+#define MAX_MIF_SEPA_SIZE 32
+#define MIF_SEPARATOR "IPC_LOGGER(VER1.1)"
+#define MIF_SEPARATOR_DPRAM "DPRAM_LOGGER(VER1.1)"
+#define MAX_IPC_SKB_SIZE 4096
+#define MAX_LOG_SIZE 64
+
+#define MAX_LOG_CNT (MAX_MIF_BUFF_SIZE / MAX_LOG_SIZE)
+#define MIF_ID_SIZE sizeof(enum mif_log_id)
+
+#define MAX_IPC_LOG_SIZE \
+ (MAX_LOG_SIZE - sizeof(enum mif_log_id) \
+ - sizeof(unsigned long long) - sizeof(size_t))
+#define MAX_IRQ_LOG_SIZE \
+ (MAX_LOG_SIZE - sizeof(enum mif_log_id) \
+ - sizeof(unsigned long long) - sizeof(struct mif_irq_map))
+#define MAX_COM_LOG_SIZE \
+ (MAX_LOG_SIZE - sizeof(enum mif_log_id) \
+ - sizeof(unsigned long long))
+#define MAX_TIM_LOG_SIZE \
+ (MAX_LOG_SIZE - sizeof(enum mif_log_id) \
+ - sizeof(unsigned long long) - sizeof(struct timespec))
+
+enum mif_log_id {
+ MIF_IPC_RL2AP = 1,
+ MIF_IPC_AP2CP,
+ MIF_IPC_CP2AP,
+ MIF_IPC_AP2RL,
+ MIF_IRQ,
+ MIF_COM,
+ MIF_TIME
+};
+
+struct mif_irq_map {
+ u16 magic;
+ u16 access;
+
+ u16 fmt_tx_in;
+ u16 fmt_tx_out;
+ u16 fmt_rx_in;
+ u16 fmt_rx_out;
+
+ u16 raw_tx_in;
+ u16 raw_tx_out;
+ u16 raw_rx_in;
+ u16 raw_rx_out;
+
+ u16 cp2ap;
+};
+
+struct mif_ipc_block {
+ enum mif_log_id id;
+ unsigned long long time;
+ size_t len;
+ char buff[MAX_IPC_LOG_SIZE];
+};
+
+struct mif_irq_block {
+ enum mif_log_id id;
+ unsigned long long time;
+ struct mif_irq_map map;
+ char buff[MAX_IRQ_LOG_SIZE];
+};
+
+struct mif_common_block {
+ enum mif_log_id id;
+ unsigned long long time;
+ char buff[MAX_COM_LOG_SIZE];
+};
+
+struct mif_time_block {
+ enum mif_log_id id;
+ unsigned long long time;
+ struct timespec epoch;
+ char buff[MAX_TIM_LOG_SIZE];
+};
+
+int mif_dump_dpram(struct io_device *);
+int mif_dump_log(struct modem_shared *, struct io_device *);
+
+#define mif_irq_log(msd, map, data, len) \
+ _mif_irq_log(MIF_IRQ, msd, map, data, len)
+#define mif_com_log(msd, format, ...) \
+ _mif_com_log(MIF_COM, msd, pr_fmt(format), ##__VA_ARGS__)
+#define mif_time_log(msd, epoch, data, len) \
+ _mif_time_log(MIF_TIME, msd, epoch, data, len)
+
+void mif_ipc_log(enum mif_log_id,
+ struct modem_shared *, const char *, size_t);
+void _mif_irq_log(enum mif_log_id,
+ struct modem_shared *, struct mif_irq_map, const char *, size_t);
+void _mif_com_log(enum mif_log_id,
+ struct modem_shared *, const char *, ...);
+void _mif_time_log(enum mif_log_id,
+ struct modem_shared *, struct timespec, const char *, size_t);
+
/** find_linkdev - find a link device
- * @commons: struct mif_common
+ * @msd: struct modem_shared *
*/
-static inline struct link_device *find_linkdev(struct mif_common *commons,
+static inline struct link_device *find_linkdev(struct modem_shared *msd,
enum modem_link link_type)
{
struct link_device *ld;
- list_for_each_entry(ld, &commons->link_dev_list, list) {
+ list_for_each_entry(ld, &msd->link_dev_list, list) {
if (ld->link_type == link_type)
return ld;
}
@@ -44,6 +140,9 @@ static inline unsigned int countbits(unsigned int n)
return i;
}
+/* print IPC message as hex string with UTC time */
+int pr_ipc(const char *str, const char *data, size_t len);
+
/* print buffer as hex string */
int pr_buffer(const char *tag, const char *data, size_t data_len,
size_t max_len);
@@ -63,41 +162,43 @@ int link_rx_flowctl_cmd(struct link_device *ld, const char *data, size_t len);
/* get iod from tree functions */
-struct io_device *get_iod_with_format(struct mif_common *commons,
+struct io_device *get_iod_with_format(struct modem_shared *msd,
enum dev_format format);
-struct io_device *get_iod_with_channel(struct mif_common *commons,
+struct io_device *get_iod_with_channel(struct modem_shared *msd,
unsigned channel);
static inline struct io_device *link_get_iod_with_format(
struct link_device *ld, enum dev_format format)
{
- struct io_device *iod = get_iod_with_format(&ld->mc->commons, format);
+ struct io_device *iod = get_iod_with_format(ld->msd, format);
return (iod && IS_CONNECTED(iod, ld)) ? iod : NULL;
}
static inline struct io_device *link_get_iod_with_channel(
struct link_device *ld, unsigned channel)
{
- struct io_device *iod = get_iod_with_channel(&ld->mc->commons, channel);
+ struct io_device *iod = get_iod_with_channel(ld->msd, channel);
return (iod && IS_CONNECTED(iod, ld)) ? iod : NULL;
}
/* insert iod to tree functions */
-struct io_device *insert_iod_with_format(struct mif_common *commons,
+struct io_device *insert_iod_with_format(struct modem_shared *msd,
enum dev_format format, struct io_device *iod);
-struct io_device *insert_iod_with_channel(struct mif_common *commons,
+struct io_device *insert_iod_with_channel(struct modem_shared *msd,
unsigned channel, struct io_device *iod);
/* iodev for each */
typedef void (*action_fn)(struct io_device *iod, void *args);
-void iodevs_for_each(struct mif_common *commons, action_fn action, void *args);
+void iodevs_for_each(struct modem_shared *msd, action_fn action, void *args);
/* netif wake/stop queue of iod */
void iodev_netif_wake(struct io_device *iod, void *args);
void iodev_netif_stop(struct io_device *iod, void *args);
/* change tx_link of raw devices */
-void rawdevs_set_tx_link(struct mif_common *commons, enum modem_link link_type);
+void rawdevs_set_tx_link(struct modem_shared *msd, enum modem_link link_type);
+
+__be32 ipv4str_to_be32(const char *ipv4str, size_t count);
void mif_add_timer(struct timer_list *timer, unsigned long expire,
void (*function)(unsigned long), unsigned long data);
@@ -181,6 +282,23 @@ void print_sipc5_link_fmt_frame(const u8 *psrc);
-------------------------------------------------------------------------*/
#define UDP_HDR_SIZE 8
-void print_ip4_packet(u8 *ip_pkt);
+void print_ip4_packet(u8 *ip_pkt, bool tx);
+bool is_dns_packet(u8 *ip_pkt);
+bool is_syn_packet(u8 *ip_pkt);
+
+int memcmp16_to_io(const void __iomem *to, void *from, int size);
+int mif_test_dpram(char *dp_name, u8 __iomem *start, u32 size);
+
+static const inline char *get_dev_name(int dev)
+{
+ if (dev == IPC_FMT)
+ return "FMT";
+ else if (dev == IPC_RAW)
+ return "RAW";
+ else if (dev == IPC_RFS)
+ return "RFS";
+ else
+ return "NONE";
+}
#endif/*__MODEM_UTILS_H__*/
diff --git a/drivers/misc/modem_if/modem_variation.h b/drivers/misc/modem_if/modem_variation.h
index 596abd1..b5ec61b 100644
--- a/drivers/misc/modem_if/modem_variation.h
+++ b/drivers/misc/modem_if/modem_variation.h
@@ -73,6 +73,18 @@ DECLARE_MODEM_INIT(mdm6600);
DECLARE_MODEM_INIT_DUMMY(mdm6600)
#endif
+#ifdef CONFIG_GSM_MODEM_ESC6270
+DECLARE_MODEM_INIT(esc6270);
+#else
+DECLARE_MODEM_INIT_DUMMY(esc6270)
+#endif
+
+#ifdef CONFIG_TDSCDMA_MODEM_SPRD8803
+DECLARE_MODEM_INIT(sprd8803);
+#else
+DECLARE_MODEM_INIT_DUMMY(sprd8803)
+#endif
+
/* link device support */
DECLARE_LINK_INIT_DUMMY(undefined)
@@ -88,6 +100,12 @@ DECLARE_LINK_INIT(dpram);
DECLARE_LINK_INIT_DUMMY(dpram)
#endif
+#ifdef CONFIG_LINK_DEVICE_PLD
+DECLARE_LINK_INIT(pld);
+#else
+DECLARE_LINK_INIT_DUMMY(pld)
+#endif
+
#ifdef CONFIG_LINK_DEVICE_SPI
DECLARE_LINK_INIT(spi);
#else
@@ -120,6 +138,8 @@ static modem_init_call modem_init_func[] = {
MODEM_INIT_CALL(cbp72),
MODEM_INIT_CALL(cmc221),
MODEM_INIT_CALL(mdm6600),
+ MODEM_INIT_CALL(esc6270),
+ MODEM_INIT_CALL(sprd8803),
MODEM_INIT_CALL(dummy),
};
@@ -132,6 +152,7 @@ static link_init_call link_init_func[] = {
LINK_INIT_CALL(usb),
LINK_INIT_CALL(hsic),
LINK_INIT_CALL(c2c),
+ LINK_INIT_CALL(pld),
};
static int call_modem_init_func(struct modem_ctl *mc, struct modem_data *pdata)
diff --git a/drivers/misc/modem_if/sipc4_io_device.c b/drivers/misc/modem_if/sipc4_io_device.c
index 94cd85b..28f95f7 100644
--- a/drivers/misc/modem_if/sipc4_io_device.c
+++ b/drivers/misc/modem_if/sipc4_io_device.c
@@ -85,6 +85,35 @@ static ssize_t store_waketime(struct device *dev,
static struct device_attribute attr_waketime =
__ATTR(waketime, S_IRUGO | S_IWUSR, show_waketime, store_waketime);
+static ssize_t show_loopback(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct miscdevice *miscdev = dev_get_drvdata(dev);
+ struct modem_shared *msd =
+ container_of(miscdev, struct io_device, miscdev)->msd;
+ unsigned char *ip = (unsigned char *)&msd->loopback_ipaddr;
+ char *p = buf;
+
+ p += sprintf(buf, "%u.%u.%u.%u\n", ip[0], ip[1], ip[2], ip[3]);
+
+ return p - buf;
+}
+
+static ssize_t store_loopback(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct miscdevice *miscdev = dev_get_drvdata(dev);
+ struct modem_shared *msd =
+ container_of(miscdev, struct io_device, miscdev)->msd;
+
+ msd->loopback_ipaddr = ipv4str_to_be32(buf, count);
+
+ return count;
+}
+
+static struct device_attribute attr_loopback =
+ __ATTR(loopback, S_IRUGO | S_IWUSR, show_loopback, store_loopback);
+
static int get_header_size(struct io_device *iod)
{
switch (iod->format) {
@@ -300,7 +329,7 @@ static int rx_hdlc_data_check(struct io_device *iod, struct link_device *ld,
* make skb for header info first
*/
if (iod->format == IPC_RFS && !hdr->frag_len) {
- skb = rx_alloc_skb(head_size, GFP_ATOMIC, iod, ld);
+ skb = rx_alloc_skb(head_size, iod, ld);
if (unlikely(!skb))
return -ENOMEM;
memcpy(skb_put(skb, head_size), hdr->hdr, head_size);
@@ -311,17 +340,11 @@ static int rx_hdlc_data_check(struct io_device *iod, struct link_device *ld,
* MAX_RXDATA_SIZE, this packet will split to
* multiple packets
*/
- if (iod->use_handover)
- alloc_size += sizeof(struct ethhdr);
-
- skb = rx_alloc_skb(alloc_size, GFP_ATOMIC, iod, ld);
+ skb = rx_alloc_skb(alloc_size, iod, ld);
if (unlikely(!skb)) {
fragdata(iod, ld)->realloc_offset = continue_len;
return -ENOMEM;
}
-
- if (iod->use_handover)
- skb_reserve(skb, sizeof(struct ethhdr));
fragdata(iod, ld)->skb_recv = skb;
}
@@ -357,7 +380,7 @@ static int rx_hdlc_data_check(struct io_device *iod, struct link_device *ld,
alloc_size = min(rest_len, MAX_RXDATA_SIZE);
- skb = rx_alloc_skb(alloc_size, GFP_ATOMIC, iod, ld);
+ skb = rx_alloc_skb(alloc_size, iod, ld);
if (unlikely(!skb)) {
fragdata(iod, ld)->realloc_offset = done_len;
return -ENOMEM;
@@ -399,8 +422,7 @@ static int rx_multi_fmt_frame(struct sk_buff *rx_skb)
return 0;
} else {
struct fmt_hdr *fh = NULL;
- skb = rx_alloc_skb(MAX_MULTI_FMT_SIZE, GFP_ATOMIC,
- iod, ld);
+ skb = rx_alloc_skb(MAX_MULTI_FMT_SIZE, iod, ld);
if (!skb) {
mif_err("<%d> alloc_skb fail\n",
__LINE__);
@@ -482,8 +504,7 @@ static int rx_multi_fmt_frame_sipc42(struct sk_buff *rx_skb)
return 0;
} else {
struct fmt_hdr *fh = NULL;
- skb = rx_alloc_skb(MAX_MULTI_FMT_SIZE, GFP_ATOMIC,
- real_iod, ld);
+ skb = rx_alloc_skb(MAX_MULTI_FMT_SIZE, real_iod, ld);
if (!skb) {
mif_err("alloc_skb fail\n");
return -ENOMEM;
@@ -631,6 +652,9 @@ static int rx_multipdp(struct sk_buff *skb)
struct io_device *real_iod = NULL;
ch = raw_header->channel;
+ if (ch == DATA_LOOPBACK_CHANNEL && ld->msd->loopback_ipaddr)
+ ch = RMNET0_CH_ID;
+
real_iod = link_get_iod_with_channel(ld, 0x20 | ch);
if (!real_iod) {
mif_err("wrong channel %d\n", ch);
@@ -828,7 +852,7 @@ static int rx_rfs_packet(struct io_device *iod, struct link_device *ld,
}
}
- skb = rx_alloc_skb(size, GFP_ATOMIC, iod, ld);
+ skb = rx_alloc_skb(size, iod, ld);
if (unlikely(!skb)) {
mif_err("alloc_skb fail\n");
return -ENOMEM;
@@ -850,6 +874,9 @@ static int io_dev_recv_data_from_link_dev(struct io_device *iod,
{
struct sk_buff *skb;
int err;
+ unsigned int alloc_size, rest_len;
+ char *cur;
+
/* check the iod(except IODEV_DUMMY) is open?
* if the iod is MULTIPDP, check this data on rx_iodev_skb_raw()
@@ -887,18 +914,39 @@ static int io_dev_recv_data_from_link_dev(struct io_device *iod,
case IPC_BOOT:
case IPC_RAMDUMP:
/* save packet to sk_buff */
- skb = rx_alloc_skb(len, GFP_ATOMIC, iod, ld);
- if (!skb) {
- mif_err("fail alloc skb (%d)\n", __LINE__);
- return -ENOMEM;
- }
+ skb = rx_alloc_skb(len, iod, ld);
+ if (skb) {
+ mif_debug("boot len : %d\n", len);
- mif_debug("boot len : %d\n", len);
+ memcpy(skb_put(skb, len), data, len);
+ skb_queue_tail(&iod->sk_rx_q, skb);
+ mif_debug("skb len : %d\n", skb->len);
- memcpy(skb_put(skb, len), data, len);
- skb_queue_tail(&iod->sk_rx_q, skb);
- mif_debug("skb len : %d\n", skb->len);
+ wake_up(&iod->wq);
+ return len;
+ }
+ /* 32KB page alloc fail case, alloc 3.5K a page.. */
+ mif_info("(%d)page fail, alloc fragment pages\n", len);
+
+ rest_len = len;
+ cur = (char *)data;
+ while (rest_len) {
+ alloc_size = min_t(unsigned int, MAX_RXDATA_SIZE,
+ rest_len);
+ skb = rx_alloc_skb(alloc_size, iod, ld);
+ if (!skb) {
+ mif_err("fail alloc skb (%d)\n", __LINE__);
+ return -ENOMEM;
+ }
+ mif_debug("boot len : %d\n", alloc_size);
+ memcpy(skb_put(skb, alloc_size), cur, alloc_size);
+ skb_queue_tail(&iod->sk_rx_q, skb);
+ mif_debug("skb len : %d\n", skb->len);
+
+ rest_len -= alloc_size;
+ cur += alloc_size;
+ }
wake_up(&iod->wq);
return len;
@@ -950,7 +998,7 @@ static void io_dev_sim_state_changed(struct io_device *iod, bool sim_online)
static int misc_open(struct inode *inode, struct file *filp)
{
struct io_device *iod = to_io_device(filp->private_data);
- struct mif_common *commons = &iod->mc->commons;
+ struct modem_shared *msd = iod->msd;
struct link_device *ld;
int ret;
filp->private_data = (void *)iod;
@@ -958,7 +1006,7 @@ static int misc_open(struct inode *inode, struct file *filp)
mif_err("iod = %s\n", iod->name);
atomic_inc(&iod->opened);
- list_for_each_entry(ld, &commons->link_dev_list, list) {
+ list_for_each_entry(ld, &msd->link_dev_list, list) {
if (IS_CONNECTED(iod, ld) && ld->init_comm) {
ret = ld->init_comm(ld, iod);
if (ret < 0) {
@@ -975,14 +1023,14 @@ static int misc_open(struct inode *inode, struct file *filp)
static int misc_release(struct inode *inode, struct file *filp)
{
struct io_device *iod = (struct io_device *)filp->private_data;
- struct mif_common *commons = &iod->mc->commons;
+ struct modem_shared *msd = iod->msd;
struct link_device *ld;
mif_err("iod = %s\n", iod->name);
atomic_dec(&iod->opened);
skb_queue_purge(&iod->sk_rx_q);
- list_for_each_entry(ld, &commons->link_dev_list, list) {
+ list_for_each_entry(ld, &msd->link_dev_list, list) {
if (IS_CONNECTED(iod, ld) && ld->terminate_comm)
ld->terminate_comm(ld, iod);
}
@@ -1002,6 +1050,9 @@ static unsigned int misc_poll(struct file *filp, struct poll_table_struct *wait)
} else if ((iod->mc->phone_state == STATE_CRASH_RESET) ||
(iod->mc->phone_state == STATE_CRASH_EXIT) ||
(iod->mc->phone_state == STATE_NV_REBUILDING) ||
+#if defined(CONFIG_SEC_DUAL_MODEM_MODE)
+ (iod->mc->phone_state == STATE_MODEM_SWITCH) ||
+#endif
(iod->mc->sim_state.changed)) {
if (iod->format == IPC_RAW) {
msleep(20);
@@ -1019,6 +1070,9 @@ static long misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
struct io_device *iod = (struct io_device *)filp->private_data;
struct link_device *ld = get_current_link(iod);
char cpinfo_buf[530] = "CP Crash ";
+ unsigned long size;
+ int ret;
+ char str[TASK_COMM_LEN];
mif_debug("cmd = 0x%x\n", cmd);
@@ -1044,8 +1098,8 @@ static long misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
return iod->mc->ops.modem_boot_off(iod->mc);
/* TODO - will remove this command after ril updated */
- case IOCTL_MODEM_START:
- mif_debug("misc_ioctl : IOCTL_MODEM_START\n");
+ case IOCTL_MODEM_BOOT_DONE:
+ mif_debug("misc_ioctl : IOCTL_MODEM_BOOT_DONE\n");
return 0;
case IOCTL_MODEM_STATUS:
@@ -1056,10 +1110,13 @@ static long misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
(p_state == STATE_CRASH_EXIT)) {
mif_err("<%s> send err state : %d\n",
iod->name, p_state);
- } else if (iod->mc->sim_state.changed) {
+ } else if (iod->mc->sim_state.changed &&
+ !strcmp(get_task_comm(str, get_current()), "rild")) {
int s_state = iod->mc->sim_state.online ?
STATE_SIM_ATTACH : STATE_SIM_DETACH;
iod->mc->sim_state.changed = false;
+
+ mif_info("SIM states (%d) to %s\n", s_state, str);
return s_state;
} else if (p_state == STATE_NV_REBUILDING) {
mif_info("send nv rebuild state : %d\n",
@@ -1074,7 +1131,7 @@ static long misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
if (iod->format != IPC_MULTI_RAW)
return -EINVAL;
- iodevs_for_each(&iod->mc->commons, iodev_netif_stop, 0);
+ iodevs_for_each(iod->msd, iodev_netif_stop, 0);
return 0;
case IOCTL_MODEM_PROTOCOL_RESUME:
@@ -1083,7 +1140,7 @@ static long misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
if (iod->format != IPC_MULTI_RAW)
return -EINVAL;
- iodevs_for_each(&iod->mc->commons, iodev_netif_wake, 0);
+ iodevs_for_each(iod->msd, iodev_netif_wake, 0);
return 0;
case IOCTL_MODEM_DUMP_START:
@@ -1113,6 +1170,38 @@ static long misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
mif_err("misc_ioctl : IOCTL_MODEM_DUMP_RESET\n");
return iod->mc->ops.modem_dump_reset(iod->mc);
+#if defined(CONFIG_SEC_DUAL_MODEM_MODE)
+ case IOCTL_MODEM_SWITCH_MODEM:
+ mif_err("misc_ioctl : IOCTL_MODEM_SWITCH_MODEM\n");
+ iod->mc->phone_state = STATE_MODEM_SWITCH;
+ wake_up(&iod->wq);
+ return 0;
+#endif
+
+ case IOCTL_MIF_LOG_DUMP:
+ size = MAX_MIF_BUFF_SIZE;
+ ret = copy_to_user((void __user *)arg, &size,
+ sizeof(unsigned long));
+ if (ret < 0)
+ return -EFAULT;
+
+ mif_dump_log(iod->mc->msd, iod);
+ return 0;
+
+ case IOCTL_MIF_DPRAM_DUMP:
+#ifdef CONFIG_LINK_DEVICE_DPRAM
+ if (iod->mc->mdm_data->link_types & LINKTYPE(LINKDEV_DPRAM)) {
+ size = iod->mc->mdm_data->dpram_ctl->dp_size;
+ ret = copy_to_user((void __user *)arg, &size,
+ sizeof(unsigned long));
+ if (ret < 0)
+ return -EFAULT;
+ mif_dump_dpram(iod);
+ return 0;
+ }
+#endif
+ return -EINVAL;
+
default:
/* If you need to handle the ioctl for specific link device,
* then assign the link ioctl handler to ld->ioctl
@@ -1246,6 +1335,8 @@ static ssize_t misc_read(struct file *filp, char *buf, size_t count,
struct io_device *iod = (struct io_device *)filp->private_data;
struct sk_buff *skb = NULL;
int pktsize = 0;
+ unsigned int rest_len, copy_len;
+ char *cur = buf;
skb = skb_dequeue(&iod->sk_rx_q);
if (!skb) {
@@ -1254,43 +1345,67 @@ static ssize_t misc_read(struct file *filp, char *buf, size_t count,
}
mif_debug("<%s> skb->len : %d\n", iod->name, skb->len);
- if (skb->len > count) {
- /* BOOT device receviced rx data as serial stream, return data
- by User requested size */
- if (iod->format == IPC_BOOT) {
- mif_err("skb->len %d > count %d\n", skb->len,
- count);
- pr_skb("BOOT-wRX", skb);
- if (copy_to_user(buf, skb->data, count) != 0) {
+ if (iod->format == IPC_BOOT) {
+ pktsize = rest_len = count;
+ while (rest_len) {
+ if (skb->len > rest_len) {
+ /* BOOT device receviced rx data as serial
+ stream, return data by User requested size */
+ mif_err("skb->len %d > count %d\n", skb->len,
+ rest_len);
+ pr_skb("BOOT-wRX", skb);
+ if (copy_to_user(cur, skb->data, rest_len)
+ != 0) {
+ dev_kfree_skb_any(skb);
+ return -EFAULT;
+ }
+ cur += rest_len;
+ skb_pull(skb, rest_len);
+ if (skb->len) {
+ mif_info("queue-head, skb->len = %d\n",
+ skb->len);
+ skb_queue_head(&iod->sk_rx_q, skb);
+ }
+ mif_debug("return %u\n", rest_len);
+ return rest_len;
+ }
+
+ copy_len = min(rest_len, skb->len);
+ if (copy_to_user(cur, skb->data, copy_len) != 0) {
dev_kfree_skb_any(skb);
return -EFAULT;
}
- skb_pull(skb, count);
- if (skb->len) {
- mif_info("queue-head, skb->len = %d\n",
- skb->len);
- skb_queue_head(&iod->sk_rx_q, skb);
- }
+ cur += skb->len;
+ dev_kfree_skb_any(skb);
+ rest_len -= copy_len;
- return count;
- } else {
- mif_err("<%s> skb->len %d > count %d\n",
- iod->name, skb->len, count);
+ if (!rest_len)
+ break;
+
+ skb = skb_dequeue(&iod->sk_rx_q);
+ if (!skb) {
+ mif_err("<%s> %d / %d sk_rx_q\n", iod->name,
+ (count - rest_len), count);
+ return count - rest_len;
+ }
+ }
+ } else {
+ if (skb->len > count) {
+ mif_err("<%s> skb->len %d > count %d\n", iod->name,
+ skb->len, count);
dev_kfree_skb_any(skb);
return -EFAULT;
}
- }
+ pktsize = skb->len;
+ if (copy_to_user(buf, skb->data, pktsize) != 0) {
+ dev_kfree_skb_any(skb);
+ return -EFAULT;
+ }
+ if (iod->format == IPC_FMT)
+ mif_debug("copied %d bytes to user\n", pktsize);
- pktsize = skb->len;
- if (copy_to_user(buf, skb->data, pktsize) != 0) {
dev_kfree_skb_any(skb);
- return -EFAULT;
}
- if (iod->format == IPC_FMT)
- mif_debug("copied %d bytes to user\n", pktsize);
-
- dev_kfree_skb_any(skb);
-
return pktsize;
}
@@ -1370,6 +1485,7 @@ static int vnet_xmit(struct sk_buff *skb, struct net_device *ndev)
struct io_device *iod = vnet->iod;
struct link_device *ld = get_current_link(iod);
struct raw_hdr hd;
+ struct iphdr *ip_header = NULL;
/* When use `handover' with Network Bridge,
* user -> TCP/IP(kernel) -> bridge device -> TCP/IP(kernel) -> this.
@@ -1383,9 +1499,17 @@ static int vnet_xmit(struct sk_buff *skb, struct net_device *ndev)
skb_pull(skb, sizeof(struct ethhdr));
}
+ /* ip loop-back */
+ ip_header = (struct iphdr *)skb->data;
+ if (iod->msd->loopback_ipaddr &&
+ ip_header->daddr == iod->msd->loopback_ipaddr) {
+ swap(ip_header->saddr, ip_header->daddr);
+ hd.channel = DATA_LOOPBACK_CHANNEL;
+ } else {
+ hd.channel = iod->id & 0x1F;
+ }
hd.len = skb->len + sizeof(hd);
hd.control = 0;
- hd.channel = iod->id & 0x1F;
headroom = sizeof(hd) + sizeof(hdlc_start);
tailroom = sizeof(hdlc_end);
@@ -1523,9 +1647,13 @@ int sipc4_init_io_device(struct io_device *iod)
ret = device_create_file(iod->miscdev.this_device,
&attr_waketime);
if (ret)
- mif_err("failed to create sysfs file : %s\n",
+ mif_err("failed to create `waketime' file : %s\n",
+ iod->name);
+ ret = device_create_file(iod->miscdev.this_device,
+ &attr_loopback);
+ if (ret)
+ mif_err("failed to create `loopback file' : %s\n",
iod->name);
-
break;
default:
diff --git a/drivers/misc/modem_if/sipc4_modem.c b/drivers/misc/modem_if/sipc4_modem.c
index 1f9ee7c..59e2de9 100644
--- a/drivers/misc/modem_if/sipc4_modem.c
+++ b/drivers/misc/modem_if/sipc4_modem.c
@@ -41,7 +41,38 @@
#define RFS_WAKE_TIME (HZ*3)
#define RAW_WAKE_TIME (HZ*6)
-static struct modem_ctl *create_modemctl_device(struct platform_device *pdev)
+static struct modem_shared *create_modem_shared_data(void)
+{
+ struct modem_shared *msd;
+
+ msd = kzalloc(sizeof(struct modem_shared), GFP_KERNEL);
+ if (!msd)
+ return NULL;
+
+ /* initialize link device list */
+ INIT_LIST_HEAD(&msd->link_dev_list);
+
+ /* initialize tree of io devices */
+ msd->iodevs_tree_chan = RB_ROOT;
+ msd->iodevs_tree_fmt = RB_ROOT;
+
+ msd->storage.cnt = 0;
+ msd->storage.addr =
+ kzalloc(MAX_MIF_BUFF_SIZE + MAX_MIF_SEPA_SIZE, GFP_KERNEL);
+ if (!msd->storage.addr) {
+ mif_err("IPC logger buff alloc failed!!\n");
+ return NULL;
+ }
+ memset(msd->storage.addr, 0, MAX_MIF_BUFF_SIZE);
+ memcpy(msd->storage.addr, MIF_SEPARATOR, MAX_MIF_SEPA_SIZE);
+ msd->storage.addr += MAX_MIF_SEPA_SIZE;
+ spin_lock_init(&msd->lock);
+
+ return msd;
+}
+
+static struct modem_ctl *create_modemctl_device(struct platform_device *pdev,
+ struct modem_shared *msd)
{
int ret = 0;
struct modem_data *pdata;
@@ -53,6 +84,7 @@ static struct modem_ctl *create_modemctl_device(struct platform_device *pdev)
if (!modemctl)
return NULL;
+ modemctl->msd = msd;
modemctl->dev = dev;
modemctl->phone_state = STATE_OFFLINE;
@@ -60,13 +92,6 @@ static struct modem_ctl *create_modemctl_device(struct platform_device *pdev)
modemctl->mdm_data = pdata;
modemctl->name = pdata->name;
- /* initialize link device list */
- INIT_LIST_HEAD(&modemctl->commons.link_dev_list);
-
- /* initialize tree of io devices */
- modemctl->commons.iodevs_tree_chan = RB_ROOT;
- modemctl->commons.iodevs_tree_fmt = RB_ROOT;
-
/* init modemctl device for getting modemctl operations */
ret = call_modem_init_func(modemctl, pdata);
if (ret) {
@@ -74,24 +99,14 @@ static struct modem_ctl *create_modemctl_device(struct platform_device *pdev)
return NULL;
}
- modemctl->use_mif_log = pdata->use_mif_log;
- if (pdata->use_mif_log) {
- mif_err("<%s> IPC logger can be used.\n",
- pdata->name);
- }
-
- ret = mif_init_log(modemctl);
- if (ret < 0) {
- kfree(modemctl);
- return NULL;
- }
-
mif_info("%s is created!!!\n", pdata->name);
+
return modemctl;
}
static struct io_device *create_io_device(struct modem_io_t *io_t,
- struct modem_ctl *modemctl, struct modem_data *pdata)
+ struct modem_shared *msd, struct modem_ctl *modemctl,
+ struct modem_data *pdata)
{
int ret = 0;
struct io_device *iod = NULL;
@@ -124,6 +139,16 @@ static struct io_device *create_io_device(struct modem_io_t *io_t,
mif_info("Bood device = %s\n", iod->name);
}
+ /* link between io device and modem shared */
+ iod->msd = msd;
+
+ /* add iod to rb_tree */
+ if (iod->format != IPC_RAW)
+ insert_iod_with_format(msd, iod->format, iod);
+
+ if (sipc4_is_not_reserved_channel(iod->id))
+ insert_iod_with_channel(msd, iod->id, iod);
+
/* register misc device or net device */
ret = sipc4_init_io_device(iod);
if (ret) {
@@ -136,20 +161,13 @@ static struct io_device *create_io_device(struct modem_io_t *io_t,
return iod;
}
-static int attach_devices(struct modem_ctl *mc, struct io_device *iod,
- enum modem_link tx_link)
+static int attach_devices(struct io_device *iod, enum modem_link tx_link)
{
+ struct modem_shared *msd = iod->msd;
struct link_device *ld;
- /* add iod to rb_tree */
- if (iod->format != IPC_RAW)
- insert_iod_with_format(&mc->commons, iod->format, iod);
-
- if (sipc4_is_not_reserved_channel(iod->id))
- insert_iod_with_channel(&mc->commons, iod->id, iod);
-
/* find link type for this io device */
- list_for_each_entry(ld, &mc->commons.link_dev_list, list) {
+ list_for_each_entry(ld, &msd->link_dev_list, list) {
if (IS_CONNECTED(iod, ld)) {
/* The count 1 bits of iod->link_types is count
* of link devices of this iod.
@@ -210,14 +228,21 @@ static int __devinit modem_probe(struct platform_device *pdev)
{
int i;
struct modem_data *pdata = pdev->dev.platform_data;
- struct modem_ctl *modemctl;
+ struct modem_shared *msd = NULL;
+ struct modem_ctl *modemctl = NULL;
struct io_device *iod[pdata->num_iodevs];
struct link_device *ld;
mif_err("%s\n", pdev->name);
memset(iod, 0, sizeof(iod));
- modemctl = create_modemctl_device(pdev);
+ msd = create_modem_shared_data();
+ if (!msd) {
+ mif_err("msd == NULL\n");
+ goto err_free_modemctl;
+ }
+
+ modemctl = create_modemctl_device(pdev, msd);
if (!modemctl) {
mif_err("modemctl == NULL\n");
goto err_free_modemctl;
@@ -235,20 +260,21 @@ static int __devinit modem_probe(struct platform_device *pdev)
mif_err("link created: %s\n", ld->name);
ld->link_type = i;
ld->mc = modemctl;
- list_add(&ld->list, &modemctl->commons.link_dev_list);
+ ld->msd = msd;
+ list_add(&ld->list, &msd->link_dev_list);
}
}
/* create io deivces and connect to modemctl device */
for (i = 0; i < pdata->num_iodevs; i++) {
- iod[i] = create_io_device(&pdata->iodevs[i], modemctl, pdata);
+ iod[i] = create_io_device(&pdata->iodevs[i], msd, modemctl,
+ pdata);
if (!iod[i]) {
mif_err("iod[%d] == NULL\n", i);
goto err_free_modemctl;
}
- attach_devices(modemctl, iod[i],
- pdata->iodevs[i].tx_link);
+ attach_devices(iod[i], pdata->iodevs[i].tx_link);
}
platform_set_drvdata(pdev, modemctl);
@@ -264,6 +290,9 @@ err_free_modemctl:
if (modemctl != NULL)
kfree(modemctl);
+ if (msd != NULL)
+ kfree(msd);
+
return -ENOMEM;
}
diff --git a/drivers/misc/modem_if/sipc5_io_device.c b/drivers/misc/modem_if/sipc5_io_device.c
index 05578f4..a9932c1 100644
--- a/drivers/misc/modem_if/sipc5_io_device.c
+++ b/drivers/misc/modem_if/sipc5_io_device.c
@@ -69,48 +69,129 @@ static ssize_t store_waketime(struct device *dev,
static struct device_attribute attr_waketime =
__ATTR(waketime, S_IRUGO | S_IWUSR, show_waketime, store_waketime);
-static inline int sipc5_check_frame_cfg(u8 *buff, struct sipc5_frame_data *frm)
+static ssize_t show_loopback(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
- u8 config = buff[0];
+ struct miscdevice *miscdev = dev_get_drvdata(dev);
+ struct modem_shared *msd =
+ container_of(miscdev, struct io_device, miscdev)->msd;
+ unsigned char *ip = (unsigned char *)&msd->loopback_ipaddr;
+ char *p = buf;
- if ((config & SIPC5_START_MASK) != SIPC5_START_MASK)
- return -EBADMSG;
+ p += sprintf(buf, "%u.%u.%u.%u\n", ip[0], ip[1], ip[2], ip[3]);
+
+ return p - buf;
+}
+
+static ssize_t store_loopback(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct miscdevice *miscdev = dev_get_drvdata(dev);
+ struct modem_shared *msd =
+ container_of(miscdev, struct io_device, miscdev)->msd;
+
+ msd->loopback_ipaddr = ipv4str_to_be32(buf, count);
+
+ return count;
+}
+
+static struct device_attribute attr_loopback =
+ __ATTR(loopback, S_IRUGO | S_IWUSR, show_loopback, store_loopback);
+
+static void iodev_showtxlink(struct io_device *iod, void *args)
+{
+ char **p = (char **)args;
+ struct link_device *ld = get_current_link(iod);
+
+ if (iod->io_typ == IODEV_NET && IS_CONNECTED(iod, ld))
+ *p += sprintf(*p, "%s: %s\n", iod->name, ld->name);
+}
+
+static ssize_t show_txlink(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct miscdevice *miscdev = dev_get_drvdata(dev);
+ struct modem_shared *msd =
+ container_of(miscdev, struct io_device, miscdev)->msd;
+ char *p = buf;
+
+ iodevs_for_each(msd, iodev_showtxlink, &p);
+
+ return p - buf;
+}
+
+static ssize_t store_txlink(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ /* don't change without gpio dynamic switching */
+ return -EINVAL;
+}
- frm->config = config;
- frm->hdr_len = SIPC5_MIN_HEADER_SIZE;
+static struct device_attribute attr_txlink =
+ __ATTR(txlink, S_IRUGO | S_IWUSR, show_txlink, store_txlink);
- if (config & SIPC5_PADDING_EXIST)
+/**
+ * rx_check_frame_cfg
+ * @cfg: configuration field of a link layer header
+ * @frm: pointer to the sipc5_frame_data buffer
+ *
+ * 1) Checks whether or not an extended field exists
+ * 2) Calculates the length of a link layer header
+ *
+ * Returns the size of a link layer header
+ *
+ * Must be invoked only when the configuration field of the link layer header
+ * is validated with sipc5_start_valid() function
+ */
+static int rx_check_frame_cfg(u8 cfg, struct sipc5_frame_data *frm)
+{
+ frm->config = cfg;
+
+ if (likely(cfg & SIPC5_PADDING_EXIST))
frm->padding = true;
- if (unlikely(config & SIPC5_EXT_FIELD_EXIST)) {
- frm->ext_fld = true;
- if (config & SIPC5_CTL_FIELD_EXIST) {
+ if (unlikely(cfg & SIPC5_EXT_FIELD_EXIST)) {
+ if (cfg & SIPC5_CTL_FIELD_EXIST) {
frm->ctl_fld = true;
frm->hdr_len = SIPC5_HEADER_SIZE_WITH_CTL_FLD;
} else {
frm->ext_len = true;
frm->hdr_len = SIPC5_HEADER_SIZE_WITH_EXT_LEN;
}
+ } else {
+ frm->hdr_len = SIPC5_MIN_HEADER_SIZE;
}
- return SIPC5_CONFIG_SIZE;
+ return frm->hdr_len;
}
-static inline void sipc5_build_rx_frame_data(struct link_device *ld,
+/**
+ * rx_build_meta_data
+ * @ld: pointer to the link device
+ * @frm: pointer to the sipc5_frame_data buffer
+ *
+ * Fills each field of sipc5_frame_data from a link layer header
+ * 1) Extracts the channel ID
+ * 2) Calculates the length of a link layer frame
+ * 3) Extracts a control field if exists
+ * 4) Calculates the length of an IPC message packet in the link layer frame
+ *
+ */
+static void rx_build_meta_data(struct link_device *ld,
struct sipc5_frame_data *frm)
{
u16 *sz16 = (u16 *)(frm->hdr + SIPC5_LEN_OFFSET);
u32 *sz32 = (u32 *)(frm->hdr + SIPC5_LEN_OFFSET);
frm->ch_id = frm->hdr[SIPC5_CH_ID_OFFSET];
- frm->len = *sz16;
- if (unlikely(frm->ext_fld)) {
- if (frm->ctl_fld)
- frm->control = frm->hdr[SIPC5_CTL_OFFSET];
- else
- frm->len = *sz32;
- }
+ if (unlikely(frm->ext_len))
+ frm->len = *sz32;
+ else
+ frm->len = *sz16;
+
+ if (unlikely(frm->ctl_fld))
+ frm->control = frm->hdr[SIPC5_CTL_OFFSET];
frm->data_len = frm->len - frm->hdr_len;
@@ -118,114 +199,83 @@ static inline void sipc5_build_rx_frame_data(struct link_device *ld,
ld->name, frm->ch_id, frm->len, frm->control, frm->data_len);
}
-static inline struct sk_buff *sipc5_prepare_rx_skb(struct io_device *iod,
- struct link_device *ld, unsigned len)
-{
- struct sk_buff *skb;
-
- if (iod->format == IPC_MULTI_RAW && iod->use_handover) {
- int alloc = len + sizeof(struct ethhdr);
- skb = rx_alloc_skb(alloc, GFP_ATOMIC, iod, ld);
- if (unlikely(!skb))
- return NULL;
- skb_reserve(skb, sizeof(struct ethhdr));
- } else {
- skb = rx_alloc_skb(len, GFP_ATOMIC, iod, ld);
- }
-
- return skb;
-}
-
-/* Check and store link layer header, then alloc an skb */
-static int sipc5_recv_header(struct io_device *iod, struct link_device *ld,
- u8 *buff, unsigned size, struct sipc5_frame_data *frm)
+/**
+ * tx_build_link_header
+ * @frm: pointer to the sipc5_frame_data buffer
+ * @iod: pointer to the IO device
+ * @ld: pointer to the link device
+ * @count: length of the data to be transmitted
+ *
+ * Builds the meta data for an SIPC5 frame and the link layer header of it
+ * Returns the link layer header length for an SIPC5 frame or 0 for other frame
+ */
+static unsigned tx_build_link_header(struct sipc5_frame_data *frm,
+ struct io_device *iod, struct link_device *ld, ssize_t count)
{
- int len = 0;
-
- mif_debug("%s: size %d\n", ld->name, size);
+ u8 *buff = frm->hdr;
+ u16 *sz16 = (u16 *)(buff + SIPC5_LEN_OFFSET);
+ u32 *sz32 = (u32 *)(buff + SIPC5_LEN_OFFSET);
- if (likely(!frm->config)) {
- len = sipc5_check_frame_cfg(buff, frm);
- if (len < 0) {
- mif_info("%s: ERR! wrong start (0x%02x)\n",
- ld->name, buff[0]);
- return len;
- }
+ memset(frm, 0, sizeof(struct sipc5_frame_data));
- /* Copy the link layer header to the header buffer */
- len = min(frm->hdr_len, size);
- memcpy(frm->hdr, buff, len);
- } else {
- /* Copy the link layer header to the header buffer */
- len = min((frm->hdr_len - frm->hdr_rcvd), size);
- memcpy((frm->hdr + frm->hdr_rcvd), buff, len);
+ if (iod->format == IPC_CMD ||
+ iod->format == IPC_BOOT ||
+ iod->format == IPC_RAMDUMP) {
+ frm->len = count;
+ return 0;
}
- frm->hdr_rcvd += len;
+ frm->config = SIPC5_START_MASK;
- mif_debug("%s: FRM hdr.len:%d hdr.rcvd:%d\n",
- ld->name, frm->hdr_len, frm->hdr_rcvd);
+ if (iod->format == IPC_FMT && count > 2048) {
+ frm->ctl_fld = true;
+ frm->config |= SIPC5_EXT_FIELD_EXIST;
+ frm->config |= SIPC5_CTL_FIELD_EXIST;
+ }
- if (frm->hdr_rcvd >= frm->hdr_len) {
- struct sk_buff *skb;
- sipc5_build_rx_frame_data(ld, frm);
- skb = sipc5_prepare_rx_skb(iod, ld, frm->data_len);
- fragdata(iod, ld)->skb_recv = skb;
+ if (iod->id >= SIPC5_CH_ID_RFS_0 && count > 0xFFFF) {
+ frm->ext_len = true;
+ frm->config |= SIPC5_EXT_FIELD_EXIST;
}
- return len;
-}
+ if (ld->aligned)
+ frm->config |= SIPC5_PADDING_EXIST;
-/* copy data to skb */
-static int sipc5_recv_payload(struct io_device *iod, struct link_device *ld,
- u8 *buff, unsigned size, struct sipc5_frame_data *frm)
-{
- struct sk_buff *skb = fragdata(iod, ld)->skb_recv;
- unsigned rest = frm->data_len - frm->data_rcvd;
- unsigned len;
+ frm->ch_id = iod->id;
- /*
- ** rest == frm->data_len - frm->data_rcvd == tailroom of skb or mifb
- */
- rest = frm->data_len - frm->data_rcvd;
- mif_debug("%s: FRM data.len:%d data.rcvd:%d rest:%d size:%d\n",
- ld->name, frm->data_len, frm->data_rcvd, rest, size);
+ frm->hdr_len = sipc5_get_hdr_len(frm->config);
+ frm->data_len = count;
+ frm->len = frm->hdr_len + frm->data_len;
- /* If there is no skb, data must be dropped. */
- len = min(rest, size);
- if (skb)
- memcpy(skb_put(skb, len), buff, len);
+ buff[SIPC5_CONFIG_OFFSET] = frm->config;
+ buff[SIPC5_CH_ID_OFFSET] = frm->ch_id;
- frm->data_rcvd += len;
+ if (unlikely(frm->ext_len))
+ *sz32 = (u32)frm->len;
+ else
+ *sz16 = (u16)frm->len;
- mif_debug("%s: FRM data.len:%d data.rcvd:%d\n",
- ld->name, frm->data_len, frm->data_rcvd);
+ if (unlikely(frm->ctl_fld))
+ buff[SIPC5_CTL_OFFSET] = frm->control;
- return len;
+ return frm->hdr_len;
}
-static int sipc5_recv_fmt(struct sk_buff *skb, struct sipc5_frame_data *frm)
+static int rx_fmt_frame(struct sk_buff *skb)
{
struct io_device *iod = skbpriv(skb)->iod;
struct link_device *ld = skbpriv(skb)->ld;
- struct sk_buff_head *rxq;
+ struct sk_buff_head *rxq = &iod->sk_rx_q;
struct sipc_fmt_hdr *fh;
struct sk_buff *rx_skb;
- unsigned id;
-
- rxq = &iod->sk_rx_q;
- if (!rxq) {
- mif_debug("%s: no sk_rx_q\n", iod->name);
- return -EINVAL;
- }
-
- id = frm->control & 0x7F;
+ u8 ctrl = skbpriv(skb)->control;
+ unsigned id = ctrl & 0x7F;
if (iod->skb[id] == NULL) {
/*
** There has been no multiple frame with this ID.
*/
- if ((frm->control & 0x80) == 0) {
+ if ((ctrl & 0x80) == 0) {
/*
** It is a single frame because the "more" bit is 0.
*/
@@ -252,7 +302,7 @@ static int sipc5_recv_fmt(struct sk_buff *skb, struct sipc5_frame_data *frm)
mif_debug("%s: start multi-frame (ID:%d len:%d)\n",
iod->name, id, fh->len);
- rx_skb = rx_alloc_skb(fh->len, GFP_ATOMIC, iod, ld);
+ rx_skb = rx_alloc_skb(fh->len, iod, ld);
if (!rx_skb) {
mif_info("%s: ERR! rx_alloc_skb fail\n", iod->name);
return -ENOMEM;
@@ -269,7 +319,7 @@ static int sipc5_recv_fmt(struct sk_buff *skb, struct sipc5_frame_data *frm)
memcpy(skb_put(rx_skb, skb->len), skb->data, skb->len);
dev_kfree_skb_any(skb);
- if (frm->control & 0x80) {
+ if (ctrl & 0x80) {
/* The last frame has not arrived yet. */
mif_debug("%s: recv multi-frame (ID:%d rcvd:%d)\n",
iod->name, id, rx_skb->len);
@@ -295,7 +345,7 @@ static int sipc5_recv_fmt(struct sk_buff *skb, struct sipc5_frame_data *frm)
return 0;
}
-static int sipc5_recv_rfs(struct sk_buff *skb)
+static int rx_rfs_frame(struct sk_buff *skb)
{
struct io_device *iod = skbpriv(skb)->iod;
struct sk_buff_head *rxq = &iod->sk_rx_q;
@@ -303,8 +353,7 @@ static int sipc5_recv_rfs(struct sk_buff *skb)
skb_queue_tail(rxq, skb);
if (unlikely(rxq->qlen > 2048)) {
struct sk_buff *victim;
- mif_debug("%s: ERR! rxq->qlen %d > 2048\n",
- iod->name, rxq->qlen);
+ mif_debug("%s: rxq->qlen %d > 2048\n", iod->name, rxq->qlen);
victim = skb_dequeue(rxq);
dev_kfree_skb_any(victim);
} else {
@@ -316,7 +365,42 @@ static int sipc5_recv_rfs(struct sk_buff *skb)
return 0;
}
-static int sipc5_recv_misc(struct sk_buff *skb)
+static int rx_loopback(struct sk_buff *skb)
+{
+ struct io_device *iod = skbpriv(skb)->iod;
+ struct link_device *ld = get_current_link(iod);
+ struct sipc5_frame_data frm;
+ unsigned headroom;
+ unsigned tailroom = 0;
+ int ret;
+
+ headroom = tx_build_link_header(&frm, iod, ld, skb->len);
+
+ if (ld->aligned)
+ tailroom = sipc5_calc_padding_size(headroom + skb->len);
+
+ /* We need not to expand skb in here. dev_alloc_skb (in rx_alloc_skb)
+ * already alloc 32bytes padding in headroom. 32bytes are enough.
+ */
+
+ /* store IPC link header to start of skb
+ * this is skb_push not skb_put. different with misc_write.
+ */
+ memcpy(skb_push(skb, headroom), frm.hdr, headroom);
+
+ /* store padding */
+ if (tailroom)
+ skb_put(skb, tailroom);
+
+ /* forward */
+ ret = ld->send(ld, iod, skb);
+ if (ret < 0)
+ mif_err("%s->%s: ld->send fail: %d\n", iod->name,
+ ld->name, ret);
+ return ret;
+}
+
+static int rx_raw_misc(struct sk_buff *skb)
{
struct io_device *iod = skbpriv(skb)->iod; /* same with real_iod */
struct sk_buff_head *rxq = &iod->sk_rx_q;
@@ -324,8 +408,7 @@ static int sipc5_recv_misc(struct sk_buff *skb)
skb_queue_tail(rxq, skb);
if (unlikely(rxq->qlen > 2048)) {
struct sk_buff *victim;
- mif_debug("%s: ERR! rxq->qlen %d > 2048\n",
- iod->name, rxq->qlen);
+ mif_debug("%s: rxq->qlen %d > 2048\n", iod->name, rxq->qlen);
victim = skb_dequeue(rxq);
dev_kfree_skb_any(victim);
} else {
@@ -337,7 +420,7 @@ static int sipc5_recv_misc(struct sk_buff *skb)
return 0;
}
-static int sipc5_recv_pdp(struct sk_buff *skb)
+static int rx_multi_pdp(struct sk_buff *skb)
{
struct io_device *iod = skbpriv(skb)->iod; /* same with real_iod */
struct net_device *ndev;
@@ -386,69 +469,24 @@ static int sipc5_recv_pdp(struct sk_buff *skb)
return ret;
}
-/** rx_iodev_work - rx workqueue for raw data
- *
- * @work: workqueue
- *
- * If you throw packets to Network layer directly in interrupt context,
- * sometimes, you'll meet backlog buffer full of Network layer.
- * Applications need some time to get packets from Network layer.
- * And, we need to retry logic when NET_RX_DROP occured. this work ensure
- * retry when netif_rx failed.
- */
-static void rx_iodev_work(struct work_struct *work)
-{
- int ret = 0;
- struct sk_buff *skb = NULL;
- struct io_device *iod = container_of(work, struct io_device,
- rx_work.work);
-
- while ((skb = skb_dequeue(&iod->sk_rx_q)) != NULL) {
- ret = sipc5_recv_pdp(skb);
- if (ret < 0) {
- mif_err("%s: sipc5_recv_pdp fail (err %d)",
- iod->name, ret);
- dev_kfree_skb_any(skb);
- } else if (ret == NET_RX_DROP) {
- mif_err("%s: ret == NET_RX_DROP. retry later.\n",
- iod->name);
- schedule_delayed_work(&iod->rx_work,
- msecs_to_jiffies(100));
- return;
- }
- }
-}
-
-static int rx_multipdp(struct sk_buff *skb)
-{
- /* in sipc5, this iod == real_iod. not MULTIPDP's iod */
- struct io_device *iod = skbpriv(skb)->iod;
-
- if (iod->io_typ != IODEV_NET) {
- mif_info("%s: ERR! wrong io_type %d\n", iod->name, iod->io_typ);
- return -EINVAL;
- }
-
- skb_queue_tail(&iod->sk_rx_q, skb);
- mif_debug("%s: sk_rx_qlen %d\n", iod->name, iod->sk_rx_q.qlen);
-
- schedule_delayed_work(&iod->rx_work, 0);
- return 0;
-}
-
-static int sipc5_recv_demux(struct link_device *ld, struct sk_buff *skb,
- struct sipc5_frame_data *frm)
+static int rx_demux(struct link_device *ld, struct sk_buff *skb)
{
struct io_device *iod = NULL;
+ char *link = ld->name;
+ u8 ch = skbpriv(skb)->ch_id;
- if (unlikely(frm->ch_id >= SIPC5_CH_ID_MAX || frm->ch_id == 0)) {
- mif_info("%s: ERR! invalid channel %d\n", ld->name, frm->ch_id);
+ if (unlikely(ch == SIPC5_CH_ID_MAX || ch == 0)) {
+ mif_info("%s: ERR! invalid ch# %d\n", link, ch);
return -ENODEV;
}
- iod = link_get_iod_with_channel(ld, frm->ch_id);
+ /* IP loopback */
+ if (ch == DATA_LOOPBACK_CHANNEL && ld->msd->loopback_ipaddr)
+ ch = RMNET0_CH_ID;
+
+ iod = link_get_iod_with_channel(ld, ch);
if (unlikely(!iod)) {
- mif_info("%s: ERR! no iod for ch# %d\n", ld->name, frm->ch_id);
+ mif_info("%s: ERR! no iod for ch# %d\n", link, ch);
return -ENODEV;
}
@@ -456,54 +494,127 @@ static int sipc5_recv_demux(struct link_device *ld, struct sk_buff *skb,
skbpriv(skb)->iod = iod;
skbpriv(skb)->real_iod = iod;
+ /* don't care about CP2AP_LOOPBACK_CHANNEL is opened */
+ if (unlikely(iod->id == CP2AP_LOOPBACK_CHANNEL))
+ return rx_loopback(skb);
+
if (atomic_read(&iod->opened) <= 0) {
- mif_info("%s: ERR! %s is not opened\n", ld->name, iod->name);
+ mif_info("%s: ERR! %s is not opened\n", link, iod->name);
return -ENODEV;
}
- if (frm->ch_id >= SIPC5_CH_ID_RFS_0)
- return sipc5_recv_rfs(skb);
- else if (frm->ch_id >= SIPC5_CH_ID_FMT_0)
- return sipc5_recv_fmt(skb, frm);
+ if (ch >= SIPC5_CH_ID_RFS_0)
+ return rx_rfs_frame(skb);
+ else if (ch >= SIPC5_CH_ID_FMT_0)
+ return rx_fmt_frame(skb);
else if (iod->io_typ == IODEV_MISC)
- return sipc5_recv_misc(skb);
+ return rx_raw_misc(skb);
else
- return rx_multipdp(skb);
+ return rx_multi_pdp(skb);
+}
+
+/* Check and store link layer header, then alloc an skb */
+static int rx_header_from_serial(struct io_device *iod, struct link_device *ld,
+ u8 *buff, unsigned size, struct sipc5_frame_data *frm)
+{
+ char *link = ld->name;
+ struct sk_buff *skb;
+ int len;
+ u8 cfg = buff[0];
+
+ mif_debug("%s: size %d\n", link, size);
+
+ if (!frm->config) {
+ if (unlikely(!sipc5_start_valid(cfg))) {
+ mif_info("%s: ERR! wrong start (0x%02x)\n", link, cfg);
+ return -EBADMSG;
+ }
+ rx_check_frame_cfg(cfg, frm);
+
+ /* Copy the link layer header to the header buffer */
+ len = min(frm->hdr_len, size);
+ memcpy(frm->hdr, buff, len);
+ } else {
+ /* Copy the link layer header to the header buffer */
+ len = min((frm->hdr_len - frm->hdr_rcvd), size);
+ memcpy((frm->hdr + frm->hdr_rcvd), buff, len);
+ }
+
+ frm->hdr_rcvd += len;
+
+ mif_debug("%s: FRM hdr_len:%d, hdr_rcvd:%d\n",
+ link, frm->hdr_len, frm->hdr_rcvd);
+
+ if (frm->hdr_rcvd >= frm->hdr_len) {
+ rx_build_meta_data(ld, frm);
+ skb = rx_alloc_skb(frm->data_len, iod, ld);
+ fragdata(iod, ld)->skb_recv = skb;
+ skbpriv(skb)->ch_id = frm->ch_id;
+ skbpriv(skb)->control = frm->control;
+ }
+
+ return len;
+}
+
+/* copy data to skb */
+static int rx_payload_from_serial(struct io_device *iod, struct link_device *ld,
+ u8 *buff, unsigned size, struct sipc5_frame_data *frm)
+{
+ struct sk_buff *skb = fragdata(iod, ld)->skb_recv;
+ char *link = ld->name;
+ unsigned rest = frm->data_len - frm->data_rcvd;
+ unsigned len;
+
+ /* rest == (frm->data_len - frm->data_rcvd) == tailroom of skb */
+ rest = frm->data_len - frm->data_rcvd;
+ mif_debug("%s: FRM data.len:%d data.rcvd:%d rest:%d size:%d\n",
+ link, frm->data_len, frm->data_rcvd, rest, size);
+
+ /* If there is no skb, data must be dropped. */
+ len = min(rest, size);
+ if (skb)
+ memcpy(skb_put(skb, len), buff, len);
+
+ frm->data_rcvd += len;
+
+ mif_debug("%s: FRM data_len:%d, data_rcvd:%d\n",
+ link, frm->data_len, frm->data_rcvd);
+
+ return len;
}
-static int sipc5_recv_ipc_from_serial(struct io_device *iod,
- struct link_device *ld, const char *data, unsigned size)
+static int rx_frame_from_serial(struct io_device *iod, struct link_device *ld,
+ const char *data, unsigned size)
{
struct sipc5_frame_data *frm = &fragdata(iod, ld)->f_data;
struct sk_buff *skb;
+ char *link = ld->name;
u8 *buff = (u8 *)data;
int rest = (int)size;
int err = 0;
int done = 0;
- mif_debug("%s: size = %d\n", ld->name, size);
+ mif_debug("%s: size = %d\n", link, size);
if (frm->hdr_rcvd >= frm->hdr_len && frm->data_rcvd < frm->data_len) {
/*
- There may be an skb or mifb (fragdata(iod, ld)->skb_recv) that
- is waiting for more IPC frame. In this case, sipc5_recv_header
- function must be skipped.
+ ** There is an skb that is waiting for more SIPC5 data.
+ ** In this case, rx_header_from_serial() must be skipped.
*/
mif_debug("%s: FRM data.len:%d data.rcvd:%d -> recv_data\n",
- ld->name, frm->data_len, frm->data_rcvd);
+ link, frm->data_len, frm->data_rcvd);
goto recv_data;
}
next_frame:
/* Receive and analyze header, then prepare an akb */
- err = done = sipc5_recv_header(iod, ld, buff, rest, frm);
+ err = done = rx_header_from_serial(iod, ld, buff, rest, frm);
if (err < 0)
goto err_exit;
buff += done;
rest -= done;
- mif_debug("%s: sipc5_recv_header() -> done:%d rest:%d\n",
- ld->name, done, rest);
+ mif_debug("%s: rx_header() -> done:%d rest:%d\n", link, done, rest);
if (rest < 0)
goto err_range;
@@ -513,15 +624,13 @@ next_frame:
recv_data:
err = 0;
- mif_debug("%s: done:%d rest:%d -> recv_payload()\n",
- ld->name, done, rest);
+ mif_debug("%s: done:%d rest:%d -> rx_payload()\n", link, done, rest);
- done = sipc5_recv_payload(iod, ld, buff, rest, frm);
+ done = rx_payload_from_serial(iod, ld, buff, rest, frm);
buff += done;
rest -= done;
- mif_debug("%s: recv_payload() -> done:%d rest:%d\n",
- ld->name, done, rest);
+ mif_debug("%s: rx_payload() -> done:%d rest:%d\n", link, done, rest);
if (rest == 0 && frm->data_rcvd < frm->data_len) {
/*
@@ -538,14 +647,14 @@ recv_data:
done = sipc5_calc_padding_size(frm->len);
if (done > rest) {
mif_info("%s: ERR! padding %d > rest %d\n",
- ld->name, done, rest);
+ link, done, rest);
goto err_exit;
}
buff += done;
rest -= done;
- mif_debug("%s: padding:%d -> rest:%d\n", ld->name, done, rest);
+ mif_debug("%s: padding:%d -> rest:%d\n", link, done, rest);
if (rest < 0)
goto err_range;
@@ -554,12 +663,12 @@ recv_data:
skb = fragdata(iod, ld)->skb_recv;
if (likely(skb)) {
- mif_debug("%s: len %d -> recv_demux()\n", ld->name, skb->len);
- err = sipc5_recv_demux(ld, skb, frm);
+ mif_debug("%s: len:%d -> rx_demux()\n", link, skb->len);
+ err = rx_demux(ld, skb);
if (err < 0)
dev_kfree_skb_any(skb);
} else {
- mif_debug("%s: len:%d -> drop\n", ld->name, skb->len);
+ mif_debug("%s: len:%d -> drop\n", link, skb->len);
}
/* initialize the skb_recv and the frame_data buffer */
@@ -578,44 +687,61 @@ err_exit:
dev_kfree_skb_any(fragdata(iod, ld)->skb_recv);
memset(frm, 0, sizeof(struct sipc5_frame_data));
fragdata(iod, ld)->skb_recv = NULL;
- mif_info("%s: ERR! clear frag\n", ld->name);
+ mif_info("%s: ERR! clear frag\n", link);
}
return err;
err_range:
- mif_info("%s: ERR! size:%d vs. rest:%d\n", ld->name, size, rest);
+ mif_info("%s: ERR! size:%d vs. rest:%d\n", link, size, rest);
return size;
}
-/* Check and store link layer header */
-static int sipc5_recv_header_from_dpram(struct link_device *ld, u8 *buff,
+/**
+ * rx_header_from_mem
+ * @ld: pointer to the link device
+ * @buff: pointer to the frame
+ * @rest: size of the frame
+ * @frm: pointer to the sipc5_frame_data buffer
+ *
+ * 1) Verifies a link layer header configuration of a frame
+ * 2) Stores the link layer header to the header buffer
+ * 3) Builds and stores the meta data of the frame into a meta data buffer
+ * 4) Verifies the length of the frame
+ *
+ * Returns SIPC5 header length
+ */
+static int rx_header_from_mem(struct link_device *ld, u8 *buff, unsigned rest,
struct sipc5_frame_data *frm)
{
- int len = sipc5_check_frame_cfg(buff, frm);
+ char *link = ld->name;
+ u8 cfg = buff[0];
- if (len < 0) {
- mif_info("%s: ERR! wrong start 0x%02x\n",
- ld->name, buff[0]);
- return len;
- } else if (len > SIPC5_MAX_HEADER_SIZE) {
- mif_info("%s: ERR! wrong header length %d\n",
- ld->name, len);
+ /* Verify link layer header configuration */
+ if (unlikely(!sipc5_start_valid(cfg))) {
+ mif_info("%s: ERR! wrong start (0x%02x)\n", link, cfg);
return -EBADMSG;
}
+ rx_check_frame_cfg(cfg, frm);
- /* Copy the link layer header to the header buffer */
- len = frm->hdr_len;
- memcpy(frm->hdr, buff, len);
+ /* Store the link layer header to the header buffer */
+ memcpy(frm->hdr, buff, frm->hdr_len);
frm->hdr_rcvd = frm->hdr_len;
- sipc5_build_rx_frame_data(ld, frm);
+ /* Build and store the meta data of this frame */
+ rx_build_meta_data(ld, frm);
- return len;
+ /* Verify frame length */
+ if (unlikely(frm->len > rest)) {
+ mif_info("%s: ERR! frame length %d > rest %d\n",
+ link, frm->len, rest);
+ return -EBADMSG;
+ }
+
+ return frm->hdr_rcvd;
}
/* copy data to skb */
-static int sipc5_recv_payload_from_dpram(struct sk_buff *skb, u8 *buff,
- unsigned len)
+static int rx_payload_from_mem(struct sk_buff *skb, u8 *buff, unsigned len)
{
/* If there is no skb, data must be dropped. */
if (skb)
@@ -623,60 +749,81 @@ static int sipc5_recv_payload_from_dpram(struct sk_buff *skb, u8 *buff,
return len;
}
-static int sipc5_recv_ipc_from_dpram(struct io_device *iod,
- struct link_device *ld, const char *data, unsigned size)
+static int rx_frame_from_mem(struct io_device *iod, struct link_device *ld,
+ const char *data, unsigned size)
{
struct sipc5_frame_data *frm = &fragdata(iod, ld)->f_data;
struct sk_buff *skb;
+ char *link = ld->name;
u8 *buff = (u8 *)data;
int rest = (int)size;
int len;
int done;
- mif_debug("%s: size = %d\n", ld->name, size);
+ mif_debug("%s: size = %d\n", link, size);
while (rest > 0) {
/* Initialize the frame data buffer */
memset(frm, 0, sizeof(struct sipc5_frame_data));
+ skb = NULL;
/* Receive and analyze link layer header */
- done = sipc5_recv_header_from_dpram(ld, buff, frm);
+ done = rx_header_from_mem(ld, buff, rest, frm);
if (unlikely(done < 0))
return -EBADMSG;
+ /* Verify rest size */
rest -= done;
- if (rest < 0)
+ if (rest < 0) {
+ mif_info("%s: ERR! rx_header -> rest %d\n", link, rest);
return -ERANGE;
+ }
+
+ /* Move buff pointer to the payload */
buff += done;
/* Prepare an akb */
len = frm->data_len;
- skb = sipc5_prepare_rx_skb(iod, ld, len);
+ skb = rx_alloc_skb(len, iod, ld);
+
+ /* Store channel ID and control fields to the CB of the skb */
+ skbpriv(skb)->ch_id = frm->ch_id;
+ skbpriv(skb)->control = frm->control;
/* Receive payload */
- mif_debug("%s: done:%d rest:%d len:%d -> recv_payload()\n",
- ld->name, done, rest, len);
- done = sipc5_recv_payload_from_dpram(skb, buff, len);
+ mif_debug("%s: done:%d rest:%d len:%d -> rx_payload()\n",
+ link, done, rest, len);
+ done = rx_payload_from_mem(skb, buff, len);
rest -= done;
- if (rest < 0)
+ if (rest < 0) {
+ mif_info("%s: ERR! rx_payload() -> rest %d\n",
+ link, rest);
+ if (skb)
+ dev_kfree_skb_any(skb);
return -ERANGE;
+ }
buff += done;
/* A padding size is applied to access the next IPC frame. */
if (frm->padding) {
done = sipc5_calc_padding_size(frm->len);
- rest -= done;
- if (rest < 0)
+ if (done > rest) {
+ mif_info("%s: ERR! padding %d > rest %d\n",
+ link, done, rest);
+ if (skb)
+ dev_kfree_skb_any(skb);
return -ERANGE;
+ }
buff += done;
+ rest -= done;
}
if (likely(skb)) {
- mif_debug("%s: len:%d -> demux\n", ld->name, skb->len);
- if (sipc5_recv_demux(ld, skb, frm) < 0)
+ mif_debug("%s: len:%d -> rx_demux()\n", link, skb->len);
+ if (rx_demux(ld, skb) < 0)
dev_kfree_skb_any(skb);
} else {
- mif_debug("%s: len:%d -> drop\n", ld->name, skb->len);
+ mif_debug("%s: len:%d -> drop\n", link, skb->len);
}
}
@@ -689,26 +836,16 @@ static int io_dev_recv_data_from_link_dev(struct io_device *iod,
{
struct sk_buff_head *rxq = &iod->sk_rx_q;
struct sk_buff *skb;
+ char *link = ld->name;
int err;
- if (!ld) {
- mif_info("ERR: !ld\n");
- return -EINVAL;
- }
-
- if (!iod) {
- mif_info("%s: ERR! !iod\n", ld->name);
- return -EINVAL;
- }
-
if (!data) {
- mif_info("%s: ERR! !data\n", ld->name);
+ mif_info("%s: ERR! !data\n", link);
return -EINVAL;
}
if (len <= 0) {
- mif_info("%s: ERR! len = %d <= 0\n",
- ld->name, len);
+ mif_info("%s: ERR! len %d <= 0\n", link, len);
return -EINVAL;
}
@@ -721,13 +858,13 @@ static int io_dev_recv_data_from_link_dev(struct io_device *iod,
wake_lock_timeout(&iod->wakelock, iod->waketime);
if (ld->link_type == LINKDEV_DPRAM && ld->aligned)
- err = sipc5_recv_ipc_from_dpram(iod, ld, data, len);
+ err = rx_frame_from_mem(iod, ld, data, len);
else
- err = sipc5_recv_ipc_from_serial(iod, ld, data, len);
+ err = rx_frame_from_serial(iod, ld, data, len);
if (err < 0)
- mif_info("%s: ERR! sipc5_recv_ipc_from_link fail "
- "(err %d)\n", ld->name, err);
+ mif_info("%s: ERR! rx_frame_from_link fail (err %d)\n",
+ link, err);
return err;
@@ -735,13 +872,13 @@ static int io_dev_recv_data_from_link_dev(struct io_device *iod,
case IPC_BOOT:
case IPC_RAMDUMP:
/* save packet to sk_buff */
- skb = rx_alloc_skb(len, GFP_ATOMIC, iod, ld);
+ skb = rx_alloc_skb(len, iod, ld);
if (!skb) {
- mif_info("%s: ERR! rx_alloc_skb fail\n", ld->name);
+ mif_info("%s: ERR! rx_alloc_skb fail\n", link);
return -ENOMEM;
}
- mif_debug("%s: len:%d -> iod:%s\n", ld->name, len, iod->name);
+ mif_debug("%s: len:%d -> iod:%s\n", link, len, iod->name);
memcpy(skb_put(skb, len), data, len);
skb_queue_tail(rxq, skb);
@@ -757,68 +894,84 @@ static int io_dev_recv_data_from_link_dev(struct io_device *iod,
return len;
default:
- mif_info("%s: ERR! unknown format %d\n", ld->name, iod->format);
+ mif_info("%s: ERR! unknown format %d\n", link, iod->format);
return -EINVAL;
}
}
-static unsigned sipc5_build_tx_link_header(struct sipc5_frame_data *frm,
- struct io_device *iod, struct link_device *ld, ssize_t count)
+static int rx_frame_from_skb(struct io_device *iod, struct link_device *ld,
+ struct sk_buff *skb)
{
- u8 *buff = frm->hdr;
- u16 *sz16 = (u16 *)(buff + SIPC5_LEN_OFFSET);
- u32 *sz32 = (u32 *)(buff + SIPC5_LEN_OFFSET);
+ struct sipc5_frame_data *frm = &fragdata(iod, ld)->f_data;
+ u8 cfg = skb->data[0];
+ /* Initialize the frame data buffer */
memset(frm, 0, sizeof(struct sipc5_frame_data));
- if (iod->format == IPC_CMD ||
- iod->format == IPC_BOOT ||
- iod->format == IPC_RAMDUMP) {
- frm->len = count;
- return 0;
- }
+ /*
+ ** The start of a link layer header has already been checked in the
+ ** link device.
+ */
- frm->config = SIPC5_START_MASK;
+ /* Analyze the configuration of the link layer header */
+ rx_check_frame_cfg(cfg, frm);
- if (iod->format == IPC_FMT && count > 2048) {
- frm->ext_fld = true;
- frm->ctl_fld = true;
+ /* Store the link layer header to the header buffer */
+ memcpy(frm->hdr, skb->data, frm->hdr_len);
+ frm->hdr_rcvd = frm->hdr_len;
- frm->config |= SIPC5_EXT_FIELD_EXIST;
- frm->config |= SIPC5_CTL_FIELD_EXIST;
- }
+ /* Build and store the meta data of this frame */
+ rx_build_meta_data(ld, frm);
- if (iod->id >= SIPC5_CH_ID_RFS_0 && count > 0xFFFF) {
- frm->ext_fld = true;
- frm->ext_len = true;
+ /*
+ ** The length of the frame has already been checked in the link device.
+ */
- frm->config |= SIPC5_EXT_FIELD_EXIST;
- }
+ /* Trim the link layer header off the frame */
+ skb_pull(skb, frm->hdr_len);
- if (ld->aligned)
- frm->config |= SIPC5_PADDING_EXIST;
+ /* Store channel ID and control fields to the CB of the skb */
+ skbpriv(skb)->ch_id = frm->ch_id;
+ skbpriv(skb)->control = frm->control;
- frm->ch_id = iod->id;
+ /* Demux the frame */
+ if (rx_demux(ld, skb) < 0) {
+ mif_err("%s: ERR! rx_demux fail\n", ld->name);
+ return -EINVAL;
+ }
- frm->hdr_len = sipc5_get_hdr_size(frm->config);
- frm->data_len = count;
- frm->len = frm->hdr_len + frm->data_len;
+ return 0;
+}
- buff[SIPC5_CONFIG_OFFSET] = frm->config;
- buff[SIPC5_CH_ID_OFFSET] = frm->ch_id;
+/* called from link device when a packet arrives for this io device */
+static int io_dev_recv_skb_from_link_dev(struct io_device *iod,
+ struct link_device *ld, struct sk_buff *skb)
+{
+ char *link = ld->name;
+ enum dev_format dev = iod->format;
+ int err;
- if (frm->ext_fld) {
- if (frm->ctl_fld) {
- *sz16 = (u16)frm->len;
- buff[SIPC5_CTL_OFFSET] = frm->control;
- } else {
- *sz32 = (u32)frm->len;
+ switch (dev) {
+ case IPC_FMT:
+ case IPC_RAW:
+ case IPC_RFS:
+ case IPC_MULTI_RAW:
+ if (iod->waketime)
+ wake_lock_timeout(&iod->wakelock, iod->waketime);
+
+ err = rx_frame_from_skb(iod, ld, skb);
+ if (err < 0) {
+ dev_kfree_skb_any(skb);
+ mif_info("%s: ERR! rx_frame_from_skb fail (err %d)\n",
+ link, err);
}
- } else {
- *sz16 = (u16)frm->len;
- }
- return frm->hdr_len;
+ return err;
+
+ default:
+ mif_info("%s: ERR! unknown device %d\n", link, dev);
+ return -EINVAL;
+ }
}
/* inform the IO device that the modem is now online or offline or
@@ -858,18 +1011,25 @@ static void io_dev_sim_state_changed(struct io_device *iod, bool sim_online)
}
}
+static void iodev_dump_status(struct io_device *iod, void *args)
+{
+ if (iod->format == IPC_RAW && iod->io_typ == IODEV_NET) {
+ struct link_device *ld = get_current_link(iod);
+ mif_com_log(iod->mc->msd, "%s: %s\n", iod->name, ld->name);
+ }
+}
+
static int misc_open(struct inode *inode, struct file *filp)
{
struct io_device *iod = to_io_device(filp->private_data);
- struct mif_common *commons = &iod->mc->commons;
+ struct modem_shared *msd = iod->msd;
struct link_device *ld;
int ret;
filp->private_data = (void *)iod;
- mif_info("%s\n", iod->name);
atomic_inc(&iod->opened);
- list_for_each_entry(ld, &commons->link_dev_list, list) {
+ list_for_each_entry(ld, &msd->link_dev_list, list) {
if (IS_CONNECTED(iod, ld) && ld->init_comm) {
ret = ld->init_comm(ld, iod);
if (ret < 0) {
@@ -880,24 +1040,27 @@ static int misc_open(struct inode *inode, struct file *filp)
}
}
+ mif_err("%s (opened %d)\n", iod->name, atomic_read(&iod->opened));
+
return 0;
}
static int misc_release(struct inode *inode, struct file *filp)
{
struct io_device *iod = (struct io_device *)filp->private_data;
- struct mif_common *commons = &iod->mc->commons;
+ struct modem_shared *msd = iod->msd;
struct link_device *ld;
- mif_info("%s\n", iod->name);
atomic_dec(&iod->opened);
skb_queue_purge(&iod->sk_rx_q);
- list_for_each_entry(ld, &commons->link_dev_list, list) {
+ list_for_each_entry(ld, &msd->link_dev_list, list) {
if (IS_CONNECTED(iod, ld) && ld->terminate_comm)
ld->terminate_comm(ld, iod);
}
+ mif_err("%s (opened %d)\n", iod->name, atomic_read(&iod->opened));
+
return 0;
}
@@ -930,6 +1093,8 @@ static long misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
struct io_device *iod = (struct io_device *)filp->private_data;
struct link_device *ld = get_current_link(iod);
char cpinfo_buf[530] = "CP Crash ";
+ unsigned long size;
+ int ret;
switch (cmd) {
case IOCTL_MODEM_ON:
@@ -952,9 +1117,12 @@ static long misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
mif_info("%s: IOCTL_MODEM_BOOT_OFF\n", iod->name);
return iod->mc->ops.modem_boot_off(iod->mc);
- case IOCTL_MODEM_START:
- mif_info("%s: IOCTL_MODEM_START\n", iod->name);
- return 0;
+ case IOCTL_MODEM_BOOT_DONE:
+ mif_err("%s: IOCTL_MODEM_BOOT_DONE\n", iod->name);
+ if (iod->mc->ops.modem_boot_done)
+ return iod->mc->ops.modem_boot_done(iod->mc);
+ else
+ return 0;
case IOCTL_MODEM_STATUS:
mif_debug("%s: IOCTL_MODEM_STATUS\n", iod->name);
@@ -983,7 +1151,7 @@ static long misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
if (iod->format != IPC_MULTI_RAW)
return -EINVAL;
- iodevs_for_each(&iod->mc->commons, iodev_netif_stop, 0);
+ iodevs_for_each(iod->msd, iodev_netif_stop, 0);
return 0;
case IOCTL_MODEM_PROTOCOL_RESUME:
@@ -993,7 +1161,7 @@ static long misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
if (iod->format != IPC_MULTI_RAW)
return -EINVAL;
- iodevs_for_each(&iod->mc->commons, iodev_netif_wake, 0);
+ iodevs_for_each(iod->msd, iodev_netif_wake, 0);
return 0;
case IOCTL_MODEM_DUMP_START:
@@ -1022,6 +1190,31 @@ static long misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
mif_info("%s: IOCTL_MODEM_DUMP_RESET\n", iod->name);
return iod->mc->ops.modem_dump_reset(iod->mc);
+ case IOCTL_MIF_LOG_DUMP:
+ iodevs_for_each(iod->msd, iodev_dump_status, 0);
+ size = MAX_MIF_BUFF_SIZE;
+ ret = copy_to_user((void __user *)arg, &size,
+ sizeof(unsigned long));
+ if (ret < 0)
+ return -EFAULT;
+
+ mif_dump_log(iod->mc->msd, iod);
+ return 0;
+
+ case IOCTL_MIF_DPRAM_DUMP:
+#ifdef CONFIG_LINK_DEVICE_DPRAM
+ if (iod->mc->mdm_data->link_types & LINKTYPE(LINKDEV_DPRAM)) {
+ size = iod->mc->mdm_data->dpram_ctl->dp_size;
+ ret = copy_to_user((void __user *)arg, &size,
+ sizeof(unsigned long));
+ if (ret < 0)
+ return -EFAULT;
+ mif_dump_dpram(iod);
+ return 0;
+ }
+#endif
+ return -EINVAL;
+
default:
/* If you need to handle the ioctl for specific link device,
* then assign the link ioctl handler to ld->ioctl
@@ -1040,18 +1233,18 @@ static ssize_t misc_write(struct file *filp, const char __user *data,
{
struct io_device *iod = (struct io_device *)filp->private_data;
struct link_device *ld = get_current_link(iod);
- struct sipc5_frame_data *frm = &iod->meta_frame;
struct sk_buff *skb;
- u8 *buff;
- unsigned headroom;
+ int ret;
+ unsigned headroom = 0;
unsigned tailroom = 0;
size_t tx_size;
- int ret;
+ struct sipc5_frame_data frm;
+ struct timespec epoch;
if (iod->format <= IPC_RFS && iod->id == 0)
return -EINVAL;
- headroom = sipc5_build_tx_link_header(frm, iod, ld, count);
+ headroom = tx_build_link_header(&frm, iod, ld, count);
if (ld->aligned)
tailroom = sipc5_calc_padding_size(headroom + count);
@@ -1066,20 +1259,19 @@ static ssize_t misc_write(struct file *filp, const char __user *data,
}
/* store IPC link header*/
- buff = skb_put(skb, headroom);
- memcpy(buff, frm->hdr, headroom);
+ memcpy(skb_put(skb, headroom), frm.hdr, headroom);
/* store IPC message */
- buff = skb_put(skb, count);
- if (copy_from_user(buff, data, count) != 0) {
+ if (copy_from_user(skb_put(skb, count), data, count) != 0) {
if (skb)
dev_kfree_skb_any(skb);
return -EFAULT;
}
if (iod->id == SIPC5_CH_ID_FMT_0) {
- mif_ipc_log(iod->mc, MIF_IOD_TX_EVT, iod, ld, buff, count);
- mif_flush_logs(ld->mc);
+ getnstimeofday(&epoch);
+ mif_time_log(iod->mc->msd, epoch, NULL, 0);
+ mif_ipc_log(MIF_IPC_RL2AP, iod->mc->msd, skb->data, skb->len);
}
/* store padding */
@@ -1112,6 +1304,7 @@ static ssize_t misc_read(struct file *filp, char *buf, size_t count,
struct sk_buff_head *rxq = &iod->sk_rx_q;
struct sk_buff *skb;
int copied = 0;
+ struct timespec epoch;
skb = skb_dequeue(rxq);
if (!skb) {
@@ -1120,9 +1313,9 @@ static ssize_t misc_read(struct file *filp, char *buf, size_t count,
}
if (iod->id == SIPC5_CH_ID_FMT_0) {
- mif_ipc_log(iod->mc, MIF_IOD_RX_EVT, iod, NULL, skb->data,
- skb->len);
- mif_flush_logs(iod->mc);
+ getnstimeofday(&epoch);
+ mif_time_log(iod->mc->msd, epoch, NULL, 0);
+ mif_ipc_log(MIF_IPC_AP2RL, iod->mc->msd, skb->data, skb->len);
}
copied = skb->len > count ? count : skb->len;
@@ -1200,7 +1393,7 @@ static int vnet_open(struct net_device *ndev)
{
struct vnet *vnet = netdev_priv(ndev);
- mif_info("%s\n", vnet->iod->name);
+ mif_err("%s\n", vnet->iod->name);
netif_start_queue(ndev);
atomic_inc(&vnet->iod->opened);
@@ -1211,10 +1404,11 @@ static int vnet_stop(struct net_device *ndev)
{
struct vnet *vnet = netdev_priv(ndev);
- mif_info("%s\n", vnet->iod->name);
+ mif_err("%s\n", vnet->iod->name);
atomic_dec(&vnet->iod->opened);
netif_stop_queue(ndev);
+ skb_queue_purge(&vnet->iod->sk_rx_q);
return 0;
}
@@ -1223,32 +1417,38 @@ static int vnet_xmit(struct sk_buff *skb, struct net_device *ndev)
struct vnet *vnet = netdev_priv(ndev);
struct io_device *iod = vnet->iod;
struct link_device *ld = get_current_link(iod);
- struct sipc5_frame_data *frm = &iod->meta_frame;
struct sk_buff *skb_new;
- unsigned headroom;
- unsigned tailroom = 0;
int ret;
-
-#if 0
- mif_ipc_log(iod->mc, MIF_IOD_TX_EVT, iod, ld, skb->data, skb->len);
- mif_flush_logs(ld->mc);
-#endif
+ unsigned headroom = 0;
+ unsigned tailroom = 0;
+ unsigned long tx_bytes = skb->len;
+ struct iphdr *ip_header = NULL;
+ struct sipc5_frame_data frm;
/* When use `handover' with Network Bridge,
- * user -> TCP/IP(kernel) -> bridge device -> TCP/IP(kernel) -> this.
- *
- * We remove the one ethernet header of skb before using skb->len,
- * because the skb has two ethernet headers.
+ * user -> bridge device(rmnet0) -> real rmnet(xxxx_rmnet0) -> here.
+ * bridge device is ethernet device unlike xxxx_rmnet(net device).
+ * We remove the an ethernet header of skb before using skb->len,
+ * because bridge device added an ethernet header to skb.
*/
if (iod->use_handover) {
if (iod->id >= PS_DATA_CH_0 && iod->id <= PS_DATA_CH_LAST)
skb_pull(skb, sizeof(struct ethhdr));
}
- headroom = sipc5_build_tx_link_header(frm, iod, ld, skb->len);
+ headroom = tx_build_link_header(&frm, iod, ld, skb->len);
+
+ /* ip loop-back */
+ ip_header = (struct iphdr *)skb->data;
+ if (iod->msd->loopback_ipaddr &&
+ ip_header->daddr == iod->msd->loopback_ipaddr) {
+ swap(ip_header->saddr, ip_header->daddr);
+ frm.ch_id = DATA_LOOPBACK_CHANNEL;
+ frm.hdr[SIPC5_CH_ID_OFFSET] = DATA_LOOPBACK_CHANNEL;
+ }
if (ld->aligned)
- tailroom = sipc5_calc_padding_size(frm->len);
+ tailroom = sipc5_calc_padding_size(frm.len);
if (skb_headroom(skb) < headroom || skb_tailroom(skb) < tailroom) {
mif_debug("%s: skb_copy_expand needed\n", iod->name);
@@ -1263,7 +1463,7 @@ static int vnet_xmit(struct sk_buff *skb, struct net_device *ndev)
skb_new = skb;
}
- memcpy(skb_push(skb_new, headroom), frm->hdr, headroom);
+ memcpy(skb_push(skb_new, headroom), frm.hdr, headroom);
if (tailroom)
skb_put(skb_new, tailroom);
@@ -1278,7 +1478,7 @@ static int vnet_xmit(struct sk_buff *skb, struct net_device *ndev)
}
ndev->stats.tx_packets++;
- ndev->stats.tx_bytes += skb->len;
+ ndev->stats.tx_bytes += tx_bytes;
return NETDEV_TX_OK;
}
@@ -1327,13 +1527,13 @@ int sipc5_init_io_device(struct io_device *iod)
/* Get data from link device */
mif_debug("%s: SIPC version = %d\n", iod->name, iod->ipc_version);
iod->recv = io_dev_recv_data_from_link_dev;
+ iod->recv_skb = io_dev_recv_skb_from_link_dev;
/* Register misc or net device */
switch (iod->io_typ) {
case IODEV_MISC:
init_waitqueue_head(&iod->wq);
skb_queue_head_init(&iod->sk_rx_q);
- INIT_DELAYED_WORK(&iod->rx_work, rx_iodev_work);
iod->miscdev.minor = MISC_DYNAMIC_MINOR;
iod->miscdev.name = iod->name;
@@ -1347,8 +1547,6 @@ int sipc5_init_io_device(struct io_device *iod)
case IODEV_NET:
skb_queue_head_init(&iod->sk_rx_q);
- INIT_DELAYED_WORK(&iod->rx_work, rx_iodev_work);
-
if (iod->use_handover)
iod->ndev = alloc_netdev(0, iod->name,
vnet_setup_ether);
@@ -1375,7 +1573,6 @@ int sipc5_init_io_device(struct io_device *iod)
case IODEV_DUMMY:
skb_queue_head_init(&iod->sk_rx_q);
- /* in sipc5, does not need rx_iodev_work on DUMMY */
iod->miscdev.minor = MISC_DYNAMIC_MINOR;
iod->miscdev.name = iod->name;
@@ -1389,7 +1586,16 @@ int sipc5_init_io_device(struct io_device *iod)
if (ret)
mif_info("%s: ERR! device_create_file fail\n",
iod->name);
-
+ ret = device_create_file(iod->miscdev.this_device,
+ &attr_loopback);
+ if (ret)
+ mif_err("failed to create `loopback file' : %s\n",
+ iod->name);
+ ret = device_create_file(iod->miscdev.this_device,
+ &attr_txlink);
+ if (ret)
+ mif_err("failed to create `txlink file' : %s\n",
+ iod->name);
break;
default:
diff --git a/drivers/misc/modem_if/sipc5_modem.c b/drivers/misc/modem_if/sipc5_modem.c
index a1287b5..f5e33d3 100644
--- a/drivers/misc/modem_if/sipc5_modem.c
+++ b/drivers/misc/modem_if/sipc5_modem.c
@@ -41,7 +41,41 @@
#define FMT_WAKE_TIME (HZ/2)
#define RAW_WAKE_TIME (HZ*6)
-static struct modem_ctl *create_modemctl_device(struct platform_device *pdev)
+static struct modem_shared *create_modem_shared_data(void)
+{
+ struct modem_shared *msd;
+ int size = MAX_MIF_BUFF_SIZE;
+
+ msd = kzalloc(sizeof(struct modem_shared), GFP_KERNEL);
+ if (!msd)
+ return NULL;
+
+ /* initialize link device list */
+ INIT_LIST_HEAD(&msd->link_dev_list);
+
+ /* initialize tree of io devices */
+ msd->iodevs_tree_chan = RB_ROOT;
+ msd->iodevs_tree_fmt = RB_ROOT;
+
+ msd->storage.cnt = 0;
+ msd->storage.addr = kzalloc(MAX_MIF_BUFF_SIZE +
+ (MAX_MIF_SEPA_SIZE * 2), GFP_KERNEL);
+ if (!msd->storage.addr) {
+ mif_err("IPC logger buff alloc failed!!\n");
+ return NULL;
+ }
+ memset(msd->storage.addr, 0, size + (MAX_MIF_SEPA_SIZE * 2));
+ memcpy(msd->storage.addr, MIF_SEPARATOR, MAX_MIF_SEPA_SIZE);
+ msd->storage.addr += MAX_MIF_SEPA_SIZE;
+ memcpy(msd->storage.addr, &size, MAX_MIF_SEPA_SIZE);
+ msd->storage.addr += MAX_MIF_SEPA_SIZE;
+ spin_lock_init(&msd->lock);
+
+ return msd;
+}
+
+static struct modem_ctl *create_modemctl_device(struct platform_device *pdev,
+ struct modem_shared *msd)
{
int ret = 0;
struct modem_data *pdata;
@@ -53,6 +87,7 @@ static struct modem_ctl *create_modemctl_device(struct platform_device *pdev)
if (!modemctl)
return NULL;
+ modemctl->msd = msd;
modemctl->dev = dev;
modemctl->phone_state = STATE_OFFLINE;
@@ -60,13 +95,6 @@ static struct modem_ctl *create_modemctl_device(struct platform_device *pdev)
modemctl->mdm_data = pdata;
modemctl->name = pdata->name;
- /* initialize link device list */
- INIT_LIST_HEAD(&modemctl->commons.link_dev_list);
-
- /* initialize tree of io devices */
- modemctl->commons.iodevs_tree_chan = RB_ROOT;
- modemctl->commons.iodevs_tree_fmt = RB_ROOT;
-
/* init modemctl device for getting modemctl operations */
ret = call_modem_init_func(modemctl, pdata);
if (ret) {
@@ -74,23 +102,14 @@ static struct modem_ctl *create_modemctl_device(struct platform_device *pdev)
return NULL;
}
- modemctl->use_mif_log = pdata->use_mif_log;
- if (pdata->use_mif_log)
- mif_err("<%s> IPC logger can be used.\n", pdata->name);
-
- ret = mif_init_log(modemctl);
- if (ret < 0) {
- kfree(modemctl);
- return NULL;
- }
-
mif_info("%s is created!!!\n", pdata->name);
return modemctl;
}
static struct io_device *create_io_device(struct modem_io_t *io_t,
- struct modem_ctl *modemctl, struct modem_data *pdata)
+ struct modem_shared *msd, struct modem_ctl *modemctl,
+ struct modem_data *pdata)
{
int ret = 0;
struct io_device *iod = NULL;
@@ -123,6 +142,16 @@ static struct io_device *create_io_device(struct modem_io_t *io_t,
mif_info("Bood device = %s\n", iod->name);
}
+ /* link between io device and modem shared */
+ iod->msd = msd;
+
+ /* add iod to rb_tree */
+ if (iod->format != IPC_RAW)
+ insert_iod_with_format(msd, iod->format, iod);
+
+ if (sipc5_is_not_reserved_channel(iod->id))
+ insert_iod_with_channel(msd, iod->id, iod);
+
/* register misc device or net device */
ret = sipc5_init_io_device(iod);
if (ret) {
@@ -135,21 +164,14 @@ static struct io_device *create_io_device(struct modem_io_t *io_t,
return iod;
}
-static int attach_devices(struct modem_ctl *mc, struct io_device *iod,
- enum modem_link tx_link)
+static int attach_devices(struct io_device *iod, enum modem_link tx_link)
{
+ struct modem_shared *msd = iod->msd;
struct link_device *ld;
unsigned ch;
- /* add iod to rb_tree */
- if (iod->format != IPC_RAW)
- insert_iod_with_format(&mc->commons, iod->format, iod);
-
- if (sipc5_is_not_reserved_channel(iod->id))
- insert_iod_with_channel(&mc->commons, iod->id, iod);
-
/* find link type for this io device */
- list_for_each_entry(ld, &mc->commons.link_dev_list, list) {
+ list_for_each_entry(ld, &msd->link_dev_list, list) {
if (IS_CONNECTED(iod, ld)) {
/* The count 1 bits of iod->link_types is count
* of link devices of this iod.
@@ -210,14 +232,21 @@ static int __devinit modem_probe(struct platform_device *pdev)
{
int i;
struct modem_data *pdata = pdev->dev.platform_data;
- struct modem_ctl *modemctl;
+ struct modem_shared *msd = NULL;
+ struct modem_ctl *modemctl = NULL;
struct io_device *iod[pdata->num_iodevs];
struct link_device *ld;
mif_err("%s\n", pdev->name);
memset(iod, 0, sizeof(iod));
- modemctl = create_modemctl_device(pdev);
+ msd = create_modem_shared_data();
+ if (!msd) {
+ mif_err("msd == NULL\n");
+ goto err_free_modemctl;
+ }
+
+ modemctl = create_modemctl_device(pdev, msd);
if (!modemctl) {
mif_err("modemctl == NULL\n");
goto err_free_modemctl;
@@ -235,20 +264,21 @@ static int __devinit modem_probe(struct platform_device *pdev)
mif_err("link created: %s\n", ld->name);
ld->link_type = i;
ld->mc = modemctl;
- list_add(&ld->list, &modemctl->commons.link_dev_list);
+ ld->msd = msd;
+ list_add(&ld->list, &msd->link_dev_list);
}
}
/* create io deivces and connect to modemctl device */
for (i = 0; i < pdata->num_iodevs; i++) {
- iod[i] = create_io_device(&pdata->iodevs[i], modemctl, pdata);
+ iod[i] = create_io_device(&pdata->iodevs[i], msd, modemctl,
+ pdata);
if (!iod[i]) {
mif_err("iod[%d] == NULL\n", i);
goto err_free_modemctl;
}
- attach_devices(modemctl, iod[i],
- pdata->iodevs[i].tx_link);
+ attach_devices(iod[i], pdata->iodevs[i].tx_link);
}
platform_set_drvdata(pdev, modemctl);
@@ -265,6 +295,9 @@ err_free_modemctl:
if (modemctl != NULL)
kfree(modemctl);
+ if (msd != NULL)
+ kfree(msd);
+
return -ENOMEM;
}
diff --git a/drivers/misc/modem_if_na/Kconfig b/drivers/misc/modem_if_na/Kconfig
new file mode 100644
index 0000000..d2679e4
--- /dev/null
+++ b/drivers/misc/modem_if_na/Kconfig
@@ -0,0 +1,30 @@
+menuconfig SEC_MODEM
+ bool "Samsung Mobile Modem Interface"
+ default n
+ ---help---
+ Samsung Modem Interface Driver.
+
+config CDMA_MODEM_CBP71
+ bool "modem chip : VIA CBP7.1"
+ depends on SEC_MODEM
+ default n
+
+config LTE_MODEM_CMC220
+ bool "modem chip : cmc220"
+ depends on SEC_MODEM
+ default n
+
+config LINK_DEVICE_DPRAM
+ bool "modem driver link device DPRAM"
+ depends on SEC_MODEM
+ default n
+
+config LINK_DEVICE_USB
+ bool "modem driver link device USB"
+ depends on SEC_MODEM
+ default n
+
+config INTERNAL_MODEM_IF
+ bool "modem feature for INTERNAL MODEM IF"
+ depends on SEC_MODEM
+ default n
diff --git a/drivers/misc/modem_if_na/Makefile b/drivers/misc/modem_if_na/Makefile
new file mode 100644
index 0000000..d680f41
--- /dev/null
+++ b/drivers/misc/modem_if_na/Makefile
@@ -0,0 +1,8 @@
+# Makefile of modem_if
+
+obj-y += modem.o modem_io_device.o modem_net_flowcontrol_device.o
+
+obj-$(CONFIG_CDMA_MODEM_CBP71) += modem_modemctl_device_cbp71.o
+obj-$(CONFIG_LTE_MODEM_CMC220) += modem_modemctl_device_cmc220.o lte_modem_bootloader.o
+obj-$(CONFIG_LINK_DEVICE_DPRAM) += modem_link_device_dpram.o
+obj-$(CONFIG_LINK_DEVICE_USB) += modem_link_device_usb.o modem_link_pm_usb.o
diff --git a/drivers/misc/modem_if_na/lte_modem_bootloader.c b/drivers/misc/modem_if_na/lte_modem_bootloader.c
new file mode 100644
index 0000000..0798b38
--- /dev/null
+++ b/drivers/misc/modem_if_na/lte_modem_bootloader.c
@@ -0,0 +1,320 @@
+/* Lte modem bootloader support for Samsung Tuna Board.
+ *
+ * Copyright (C) 2011 Google, Inc.
+ * 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 <linux/platform_data/lte_modem_bootloader.h>
+
+#define LEN_XMIT_DELEY 10
+#define MAX_XMIT_SIZE 16
+
+int factory_mode;
+
+enum xmit_bootloader_status {
+ XMIT_BOOT_READY,
+ XMIT_LOADER_READY,
+};
+
+struct lte_modem_bootloader {
+ struct spi_device *spi_dev;
+ struct miscdevice dev;
+
+ struct mutex lock;
+
+ unsigned int gpio_lte2ap_status;
+ enum xmit_bootloader_status xmit_status;
+};
+#define to_loader(misc) container_of(misc, struct lte_modem_bootloader, dev);
+
+static inline
+int spi_xmit(struct lte_modem_bootloader *loader,
+ const char *buf, int size_per_xmit)
+{
+ int i;
+ int ret;
+ unsigned char xmit_buf[MAX_XMIT_SIZE];
+ struct spi_message msg;
+ struct spi_transfer xfers[MAX_XMIT_SIZE];
+
+ memcpy(xmit_buf, buf, sizeof(xmit_buf));
+ spi_message_init(&msg);
+ memset(xfers, 0, sizeof(xfers));
+ for (i = 0; i < size_per_xmit ; i++) {
+ xfers[i].cs_change = 1;
+ xfers[i].len = 1;
+ xfers[i].tx_buf = xmit_buf + i;
+ spi_message_add_tail(&xfers[i], &msg);
+ }
+ ret = spi_sync(loader->spi_dev, &msg);
+
+ if (ret < 0)
+ dev_err(&loader->spi_dev->dev,
+ "%s - error %d\n", __func__, ret);
+
+ return ret;
+}
+
+
+static
+int bootloader_write(struct lte_modem_bootloader *loader,
+ const char *addr, const int len)
+{
+ int i;
+ int ret = 0;
+ unsigned char lenbuf[4];
+
+ if (loader->xmit_status == XMIT_LOADER_READY) {
+ memcpy(lenbuf, &len, ARRAY_SIZE(lenbuf));
+ ret = spi_xmit(loader, lenbuf,
+ ARRAY_SIZE(lenbuf));
+ if (ret < 0)
+ return ret;
+ msleep(LEN_XMIT_DELEY);
+ }
+
+ for (i = 0 ; i < len / MAX_XMIT_SIZE ; i++) {
+ ret = spi_xmit(loader,
+ addr + i * MAX_XMIT_SIZE,
+ MAX_XMIT_SIZE);
+ if (ret < 0)
+ return ret;
+ }
+ ret = spi_xmit(loader, addr + i * MAX_XMIT_SIZE , len % MAX_XMIT_SIZE);
+
+ return 0;
+}
+
+
+static
+int bootloader_open(struct inode *inode, struct file *flip)
+{
+ struct lte_modem_bootloader *loader = to_loader(flip->private_data);
+ flip->private_data = loader;
+
+ return 0;
+}
+
+static
+long bootloader_ioctl(struct file *flip,
+ unsigned int cmd, unsigned long arg)
+{
+ int ret = 0;
+ int status;
+ struct lte_modem_bootloader_param param;
+ struct lte_modem_bootloader *loader = flip->private_data;
+
+ mutex_lock(&loader->lock);
+ switch (cmd) {
+ case IOCTL_LTE_MODEM_XMIT_BOOT:
+
+ ret = copy_from_user(&param, (const void __user *)arg,
+ sizeof(param));
+ if (ret) {
+ dev_err(&loader->spi_dev->dev, "%s - can not copy userdata\n",
+ __func__);
+ ret = -EFAULT;
+ goto exit_err;
+ }
+
+ dev_info(&loader->spi_dev->dev,
+ "IOCTL_LTE_MODEM_XMIT_BOOT - bin size: %d\n",
+ param.len);
+
+ ret = bootloader_write(loader, param.buf, param.len);
+ if (ret < 0) {
+ dev_err(&loader->spi_dev->dev, "failed to xmit boot bin\n");
+ } else {
+ if (loader->xmit_status == XMIT_BOOT_READY)
+ loader->xmit_status = XMIT_LOADER_READY;
+ else
+ loader->xmit_status = XMIT_BOOT_READY;
+ }
+
+ break;
+ case IOCTL_LTE_MODEM_LTE2AP_STATUS:
+ status = gpio_get_value(loader->gpio_lte2ap_status);
+ pr_debug("LTE2AP status :%d\n", status);
+ ret = copy_to_user((unsigned int *)arg, &status,
+ sizeof(status));
+
+ break;
+
+ case IOCTL_LTE_MODEM_FACTORY_MODE_ON:
+ factory_mode = 1;
+ pr_info("usb %s, Factory Mode On\n", __func__);
+ break;
+
+ case IOCTL_LTE_MODEM_FACTORY_MODE_OFF:
+ factory_mode = 0;
+ pr_info("usb %s, Factory Mode Off\n", __func__);
+ break;
+
+ default:
+ dev_err(&loader->spi_dev->dev,
+ "%s - ioctl cmd error\n",
+ __func__);
+ ret = -ENOIOCTLCMD;
+
+ break;
+ }
+ mutex_unlock(&loader->lock);
+
+exit_err:
+ return ret;
+}
+
+static const struct file_operations lte_modem_bootloader_fops = {
+ .owner = THIS_MODULE,
+ .open = bootloader_open,
+ .unlocked_ioctl = bootloader_ioctl,
+};
+
+static
+int bootloader_gpio_setup(struct lte_modem_bootloader *loader)
+{
+ if (!loader->gpio_lte2ap_status)
+ return -EINVAL;
+
+ gpio_request(loader->gpio_lte2ap_status, "GPIO_LTE2AP_STATUS");
+ gpio_direction_input(loader->gpio_lte2ap_status);
+
+ return 0;
+}
+
+static
+int __devinit lte_modem_bootloader_probe(struct spi_device *spi)
+{
+ int ret;
+
+ struct lte_modem_bootloader *loader;
+ struct lte_modem_bootloader_platform_data *pdata;
+
+ loader = kzalloc(sizeof(*loader), GFP_KERNEL);
+ if (!loader) {
+ pr_err("failed to allocate for lte_modem_bootloader\n");
+ ret = -ENOMEM;
+ goto err_alloc;
+ }
+ mutex_init(&loader->lock);
+
+ spi->bits_per_word = 8;
+
+ if (spi_setup(spi)) {
+ pr_err("failed to setup spi for lte_modem_bootloader\n");
+ ret = -EINVAL;
+ goto err_setup;
+ }
+
+ loader->spi_dev = spi;
+
+ if (!spi->dev.platform_data) {
+ pr_err("failed to get platform data for lte_modem_bootloader\n");
+ ret = -EINVAL;
+ goto err_setup;
+ }
+ pdata = (struct lte_modem_bootloader_platform_data *) \
+ spi->dev.platform_data;
+ loader->gpio_lte2ap_status = pdata->gpio_lte2ap_status;
+
+ ret = bootloader_gpio_setup(loader);
+ if (ret) {
+ pr_err("failed to set gpio for lte_modem_boot_loader\n");
+ goto err_setup;
+ }
+
+ loader->gpio_lte2ap_status = pdata->gpio_lte2ap_status;
+ loader->xmit_status = XMIT_BOOT_READY;
+
+ spi_set_drvdata(spi, loader);
+
+ loader->dev.minor = MISC_DYNAMIC_MINOR;
+ loader->dev.name = "lte_spi";
+ loader->dev.fops = &lte_modem_bootloader_fops;
+ ret = misc_register(&loader->dev);
+ if (ret) {
+ pr_err("failed to register misc dev for lte_modem_bootloader\n");
+ goto err_setup;
+ }
+ pr_info("lte_modem_bootloader successfully probed\n");
+
+ factory_mode = 0;
+
+ return 0;
+
+err_setup:
+ mutex_destroy(&loader->lock);
+ kfree(loader);
+
+err_alloc:
+
+ return ret;
+}
+
+static
+int __devexit lte_modem_bootloader_remove(struct spi_device *spi)
+{
+ struct lte_modem_bootloader *loader = spi_get_drvdata(spi);
+
+ misc_deregister(&loader->dev);
+ mutex_destroy(&loader->lock);
+ kfree(loader);
+
+ return 0;
+}
+
+static
+struct spi_driver lte_modem_bootloader_driver = {
+ .driver = {
+ .name = "lte_modem_spi",
+ .bus = &spi_bus_type,
+ .owner = THIS_MODULE,
+ },
+ .probe = lte_modem_bootloader_probe,
+ .remove = __devexit_p(lte_modem_bootloader_remove),
+};
+
+static
+int __init lte_modem_bootloader_init(void)
+{
+ return spi_register_driver(&lte_modem_bootloader_driver);
+}
+
+static
+void __exit lte_modem_bootloader_exit(void)
+{
+ spi_unregister_driver(&lte_modem_bootloader_driver);
+}
+
+module_init(lte_modem_bootloader_init);
+module_exit(lte_modem_bootloader_exit);
+
+MODULE_DESCRIPTION("LTE Modem Bootloader driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/misc/modem_if_na/modem.c b/drivers/misc/modem_if_na/modem.c
new file mode 100644
index 0000000..dde1ea1
--- /dev/null
+++ b/drivers/misc/modem_if_na/modem.c
@@ -0,0 +1,221 @@
+/* linux/drivers/modem/modem.c
+ *
+ * Copyright (C) 2010 Google, Inc.
+ * Copyright (C) 2010 Samsung Electronics.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/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/io.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/delay.h>
+#include <linux/wakelock.h>
+
+#include <linux/platform_data/modem_na.h>
+#include "modem_prj.h"
+#include "modem_variation.h"
+
+
+static struct modem_ctl *create_modemctl_device(struct platform_device *pdev)
+{
+ int ret = 0;
+ struct modem_data *pdata;
+ struct modem_ctl *modemctl;
+ struct device *dev = &pdev->dev;
+
+ /* create modem control device */
+ modemctl = kzalloc(sizeof(struct modem_ctl), GFP_KERNEL);
+ if (!modemctl)
+ return NULL;
+
+ modemctl->dev = dev;
+ modemctl->phone_state = STATE_OFFLINE;
+
+ pdata = pdev->dev.platform_data;
+ modemctl->name = pdata->name;
+
+ /* init modemctl device for getting modemctl operations */
+ ret = call_modem_init_func(modemctl, pdata);
+ if (ret) {
+ printk(KERN_ERR "[MODEM_IF] call_modem_init_func is failed\n");
+ kfree(modemctl);
+ return NULL;
+ }
+
+ pr_debug("[MODEM_IF] %s:create_modemctl_device DONE\n", modemctl->name);
+ return modemctl;
+}
+
+static struct io_device *create_io_device(struct modem_io_t *io_t,
+ struct modem_ctl *modemctl, enum modem_network modem_net)
+{
+ int ret = 0;
+ struct io_device *iod = NULL;
+
+ iod = kzalloc(sizeof(struct io_device), GFP_KERNEL);
+ if (!iod) {
+ pr_err("[MODEM_IF] io device memory alloc fail\n");
+ return NULL;
+ }
+
+ iod->name = io_t->name;
+ iod->id = io_t->id;
+ iod->format = io_t->format;
+ iod->io_typ = io_t->io_type;
+ iod->net_typ = modem_net;
+
+ /* link between io device and modem control */
+ iod->mc = modemctl;
+ if (iod->format == IPC_FMT)
+ modemctl->iod = iod;
+
+ /* register misc device or net device */
+ ret = init_io_device(iod);
+ if (ret) {
+ kfree(iod);
+ return NULL;
+ }
+
+ pr_debug("[MODEM_IF] %s : create_io_device DONE\n", io_t->name);
+ return iod;
+}
+static int __devinit modem_probe(struct platform_device *pdev)
+{
+ int i;
+ struct modem_data *pdata;
+ struct modem_ctl *modemctl;
+ struct io_device *iod[MAX_NUM_IO_DEV];
+ struct link_device *ld;
+ struct io_raw_devices *io_raw_devs = NULL;
+
+ pdata = pdev->dev.platform_data;
+ memset(iod, 0, sizeof(iod));
+
+ modemctl = create_modemctl_device(pdev);
+ if (!modemctl) {
+ printk(KERN_ERR "[MODEM_IF] modemctl is null\n");
+ return -ENOMEM;
+ }
+ /* create link device */
+ ld = call_link_init_func(pdev, pdata->link_type);
+ if (!ld)
+ goto err_free_modemctl;
+
+ io_raw_devs = kzalloc(sizeof(struct io_raw_devices), GFP_KERNEL);
+ if (!io_raw_devs) {
+ printk(KERN_ERR "[MODEM_IF] io_raw_devs is null\n");
+ return -ENOMEM;
+ }
+
+ /* create io deivces and connect to modemctl device */
+ for (i = 0; i < pdata->num_iodevs; i++) {
+ iod[i] = create_io_device(&pdata->iodevs[i], modemctl,
+ pdata->modem_net);
+ if (!iod[i])
+ goto err_free_modemctl;
+
+ if (iod[i]->format == IPC_RAW) {
+ int ch = iod[i]->id & 0x1F;
+ io_raw_devs->raw_devices[ch] = iod[i];
+ io_raw_devs->num_of_raw_devs++;
+ iod[i]->link = ld;
+ } else {
+ /* connect io devices to one link device */
+ ld->attach(ld, iod[i]);
+ }
+
+ if (iod[i]->format == IPC_MULTI_RAW)
+ iod[i]->private_data = (void *)io_raw_devs;
+ }
+
+ platform_set_drvdata(pdev, modemctl);
+
+ pr_debug("[MODEM_IF] modem_probe DONE\n");
+ return 0;
+
+err_free_modemctl:
+ for (i = 0; i < pdata->num_iodevs; i++)
+ if (iod[i] != NULL)
+ kfree(iod[i]);
+
+ if (io_raw_devs != NULL)
+ kfree(io_raw_devs);
+
+ if (modemctl != NULL)
+ kfree(modemctl);
+
+ return -ENOMEM;
+}
+
+static void modem_shutdown(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct modem_ctl *mc = dev_get_drvdata(dev);
+
+ if (!mc)
+ return;
+
+ free_irq(mc->irq_phone_active, mc);
+
+ if (mc->ops.modem_off)
+ mc->ops.modem_off(mc);
+}
+
+static int modem_suspend(struct device *pdev)
+{
+ struct modem_ctl *mc = dev_get_drvdata(pdev);
+ gpio_set_value(mc->gpio_pda_active, 0);
+ return 0;
+}
+
+static int modem_resume(struct device *pdev)
+{
+ struct modem_ctl *mc = dev_get_drvdata(pdev);
+ gpio_set_value(mc->gpio_pda_active, 1);
+ return 0;
+}
+
+static const struct dev_pm_ops modem_pm_ops = {
+ .suspend = modem_suspend,
+ .resume = modem_resume,
+};
+
+static struct platform_driver modem_driver = {
+ .probe = modem_probe,
+ .shutdown = modem_shutdown,
+ .driver = {
+ .name = "modem_if",
+ .pm = &modem_pm_ops,
+ },
+};
+
+static int __init modem_init(void)
+{
+ return platform_driver_register(&modem_driver);
+}
+
+module_init(modem_init);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Samsung Modem Interface Driver");
diff --git a/drivers/misc/modem_if_na/modem_io_device.c b/drivers/misc/modem_if_na/modem_io_device.c
new file mode 100644
index 0000000..1a936b1
--- /dev/null
+++ b/drivers/misc/modem_if_na/modem_io_device.c
@@ -0,0 +1,965 @@
+/* /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
+ * 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/ratelimit.h>
+
+#include <linux/platform_data/modem_na.h>
+#include "modem_prj.h"
+
+
+#define HDLC_START 0x7F
+#define HDLC_END 0x7E
+#define SIZE_OF_HDLC_START 1
+#define SIZE_OF_HDLC_END 1
+#define MAX_RXDATA_SIZE (4096 - 512)
+
+static const char hdlc_start[1] = { HDLC_START };
+static const char hdlc_end[1] = { HDLC_END };
+
+struct fmt_hdr {
+ u16 len;
+ u8 control;
+} __packed;
+
+struct raw_hdr {
+ u32 len;
+ u8 channel;
+ u8 control;
+} __packed;
+
+struct rfs_hdr {
+ u32 len;
+ u8 cmd;
+ u8 id;
+} __packed;
+
+static const char const *modem_state_name[] = {
+ [STATE_OFFLINE] = "OFFLINE",
+ [STATE_CRASH_EXIT] = "CRASH_EXIT",
+ [STATE_BOOTING] = "BOOTING",
+ [STATE_ONLINE] = "ONLINE",
+ [STATE_LOADER_DONE] = "LOADER_DONE",
+ [STATE_NV_REBUILDING] = "NV_REBUILDING",
+};
+
+static int rx_iodev_skb(struct io_device *iod);
+
+static int get_header_size(struct io_device *iod)
+{
+ switch (iod->format) {
+ case IPC_FMT:
+ return sizeof(struct fmt_hdr);
+
+ case IPC_RAW:
+ case IPC_MULTI_RAW:
+ return sizeof(struct raw_hdr);
+
+ case IPC_RFS:
+ return sizeof(struct rfs_hdr);
+
+ case IPC_BOOT:
+ /* minimum size for transaction align */
+ return 4;
+
+ case IPC_RAMDUMP:
+ default:
+ return 0;
+ }
+}
+
+static int get_hdlc_size(struct io_device *iod, char *buf)
+{
+ struct fmt_hdr *fmt_header;
+ struct raw_hdr *raw_header;
+ struct rfs_hdr *rfs_header;
+
+ pr_debug("[MODEM_IF] buf : %02x %02x %02x (%d)\n", *buf, *(buf + 1),
+ *(buf + 2), __LINE__);
+
+ switch (iod->format) {
+ case IPC_FMT:
+ fmt_header = (struct fmt_hdr *)buf;
+ return fmt_header->len;
+ case IPC_RAW:
+ case IPC_MULTI_RAW:
+ raw_header = (struct raw_hdr *)buf;
+ return raw_header->len;
+ case IPC_RFS:
+ rfs_header = (struct rfs_hdr *)buf;
+ return rfs_header->len;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static void *get_header(struct io_device *iod, size_t count,
+ char *frame_header_buf)
+{
+ struct fmt_hdr *fmt_h;
+ struct raw_hdr *raw_h;
+ struct rfs_hdr *rfs_h;
+
+ switch (iod->format) {
+ case IPC_FMT:
+ fmt_h = (struct fmt_hdr *)frame_header_buf;
+
+ fmt_h->len = count + sizeof(struct fmt_hdr);
+ fmt_h->control = 0;
+
+ return (void *)frame_header_buf;
+
+ case IPC_RAW:
+ case IPC_MULTI_RAW:
+ raw_h = (struct raw_hdr *)frame_header_buf;
+
+ raw_h->len = count + sizeof(struct raw_hdr);
+ raw_h->channel = iod->id & 0x1F;
+ raw_h->control = 0;
+
+ return (void *)frame_header_buf;
+
+ case IPC_RFS:
+ rfs_h = (struct rfs_hdr *)frame_header_buf;
+
+ rfs_h->len = count + sizeof(struct raw_hdr);
+ rfs_h->id = iod->id;
+
+ return (void *)frame_header_buf;
+
+ default:
+ return 0;
+ }
+}
+
+static inline int rx_hdlc_head_start_check(char *buf)
+{
+ /* check hdlc head and return size of start byte */
+ return (buf[0] == HDLC_START) ? SIZE_OF_HDLC_START : -EBADMSG;
+}
+
+static inline int rx_hdlc_tail_check(char *buf)
+{
+ /* check hdlc tail and return size of tail byte */
+ return (buf[0] == HDLC_END) ? SIZE_OF_HDLC_END : -EBADMSG;
+}
+
+/* remove hdlc header and store IPC header */
+static int rx_hdlc_head_check(struct io_device *iod, char *buf, unsigned rest)
+{
+ struct header_data *hdr = &iod->h_data;
+ int head_size = get_header_size(iod);
+ int done_len = 0;
+ int len = 0;
+ struct modem_data *md = (struct modem_data *)\
+ iod->mc->dev->platform_data;
+
+ /* first frame, remove start header 7F */
+ if (!hdr->start) {
+ len = rx_hdlc_head_start_check(buf);
+ if (len < 0) {
+ pr_err("[MODEM_IF] Wrong HDLC start: 0x%x(%s)\n",
+ *buf, iod->name);
+ return len; /*Wrong hdlc start*/
+ }
+
+ pr_debug("[MODEM_IF] check len : %d, rest : %d (%d)\n", len,
+ rest, __LINE__);
+
+ /* set the start flag of current packet */
+ hdr->start = HDLC_START;
+ hdr->len = 0;
+
+ buf += len;
+ done_len += len;
+ rest -= len; /* rest, call by value */
+ }
+
+ pr_debug("[MODEM_IF] check len : %d, rest : %d (%d)\n", len, rest,
+ __LINE__);
+
+ /* store the IPC header to iod priv */
+ if (hdr->len < head_size) {
+ len = min(rest, head_size - hdr->len);
+ memcpy(hdr->hdr + hdr->len, buf, len);
+
+ /* Skip the dummy byte inserted for 2-byte alignment in header.
+ RAW format header size is 6 bytes. Start + 6 + 1 (skip byte) */
+ if (md->align == 1) {
+ if ((iod->format == IPC_RAW
+ || iod->format == IPC_MULTI_RAW)
+ && (iod->net_typ == CDMA_NETWORK)
+ && !(len & 0x01))
+ len++;
+ }
+ hdr->len += len;
+ done_len += len;
+ }
+
+ pr_debug("[MODEM_IF] check done_len : %d, rest : %d (%d)\n", done_len,
+ rest, __LINE__);
+ return done_len;
+}
+
+/* alloc skb and copy dat to skb */
+static int rx_hdlc_data_check(struct io_device *iod, char *buf, unsigned rest)
+{
+ struct header_data *hdr = &iod->h_data;
+ struct sk_buff *skb = iod->skb_recv;
+ int head_size = get_header_size(iod);
+ int data_size = get_hdlc_size(iod, hdr->hdr) - head_size;
+ int alloc_size = min(data_size, MAX_RXDATA_SIZE);
+ int len;
+ int done_len = 0;
+ int rest_len = data_size - hdr->flag_len;
+
+ /* first payload data - alloc skb */
+ if (!skb) {
+ switch (iod->format) {
+ case IPC_RFS:
+ alloc_size = min(data_size + head_size, \
+ MAX_RXDATA_SIZE);
+ skb = alloc_skb(alloc_size, GFP_ATOMIC);
+ if (unlikely(!skb))
+ return -ENOMEM;
+ /* copy the RFS haeder to skb->data */
+ memcpy(skb_put(skb, head_size), hdr->hdr, head_size);
+ break;
+
+ case IPC_MULTI_RAW:
+ if (data_size > MAX_RXDATA_SIZE) { \
+ pr_err("%s: %s: packet size too large (%d)\n",\
+ __func__, iod->name, data_size);
+ return -EINVAL;
+ }
+
+ if (iod->net_typ == UMTS_NETWORK)
+ skb = alloc_skb(alloc_size, GFP_ATOMIC);
+ else
+ skb = alloc_skb(alloc_size +
+ sizeof(struct ethhdr), GFP_ATOMIC);
+ if (unlikely(!skb))
+ return -ENOMEM;
+
+ if (iod->net_typ != UMTS_NETWORK)
+ skb_reserve(skb, sizeof(struct ethhdr));
+ break;
+
+ default:
+ skb = alloc_skb(alloc_size, GFP_ATOMIC);
+ if (unlikely(!skb))
+ return -ENOMEM;
+ break;
+ }
+ iod->skb_recv = skb;
+ }
+
+ while (rest > 0) {
+ len = min(rest, alloc_size - skb->len);
+ len = min(len, rest_len);
+ memcpy(skb_put(skb, len), buf, len);
+ buf += len;
+ done_len += len;
+ hdr->flag_len += len;
+ rest -= len;
+ rest_len -= len;
+
+ if (!rest_len || !rest)
+ break;
+
+ rx_iodev_skb(iod);
+ iod->skb_recv = NULL;
+
+ alloc_size = min(rest_len, MAX_RXDATA_SIZE);
+ skb = alloc_skb(alloc_size, GFP_ATOMIC);
+ if (unlikely(!skb))
+ return -ENOMEM;
+ iod->skb_recv = skb;
+ }
+
+ return done_len;
+}
+
+static int rx_iodev_skb_raw(struct io_device *iod)
+{
+ int err;
+ struct sk_buff *skb = iod->skb_recv;
+ struct net_device *ndev;
+ struct iphdr *ip_header;
+ struct ethhdr *ehdr;
+ const char source[ETH_ALEN] = SOURCE_MAC_ADDR;
+
+ switch (iod->io_typ) {
+ case IODEV_MISC:
+ skb_queue_tail(&iod->sk_rx_q, iod->skb_recv);
+ wake_up(&iod->wq);
+ return 0;
+
+ case IODEV_NET:
+ ndev = iod->ndev;
+ if (!ndev)
+ return NET_RX_DROP;
+
+ skb->dev = ndev;
+ ndev->stats.rx_packets++;
+ ndev->stats.rx_bytes += skb->len;
+
+ /* check the version of IP */
+ ip_header = (struct iphdr *)skb->data;
+ if (ip_header->version == IP6VERSION)
+ skb->protocol = htons(ETH_P_IPV6);
+ else
+ skb->protocol = htons(ETH_P_IP);
+
+ if (iod->net_typ == UMTS_NETWORK) {
+ skb_reset_mac_header(skb);
+ } else {
+ ehdr = (void *)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_NONE;
+ skb_reset_mac_header(skb);
+
+ skb_pull(skb, sizeof(struct ethhdr));
+ }
+
+ err = netif_rx_ni(skb);
+ if (err != NET_RX_SUCCESS)
+ dev_err(&ndev->dev, "rx error: %d\n", err);
+ return err;
+
+ default:
+ pr_err("[MODEM_IF] wrong io_type : %d\n", iod->io_typ);
+ return -EINVAL;
+ }
+}
+
+static void rx_iodev_work(struct work_struct *work)
+{
+ int ret;
+ struct sk_buff *skb;
+ struct io_device *real_iod;
+ struct io_device *iod = container_of(work, struct io_device,
+ rx_work.work);
+
+ skb = skb_dequeue(&iod->sk_rx_q);
+ while (skb) {
+ real_iod = *((struct io_device **)skb->cb);
+ real_iod->skb_recv = skb;
+
+ ret = rx_iodev_skb_raw(real_iod);
+ if (ret == NET_RX_DROP) {
+ pr_err("[MODEM_IF] %s: queue delayed work!\n",
+ __func__);
+ skb_queue_head(&iod->sk_rx_q, skb);
+ schedule_delayed_work(&iod->rx_work,
+ msecs_to_jiffies(20));
+ break;
+ } else if (ret < 0)
+ dev_kfree_skb_any(skb);
+
+ skb = skb_dequeue(&iod->sk_rx_q);
+ }
+}
+
+
+static int rx_multipdp(struct io_device *iod)
+{
+ u8 ch;
+ struct raw_hdr *raw_header = (struct raw_hdr *)&iod->h_data.hdr;
+ struct io_raw_devices *io_raw_devs =
+ (struct io_raw_devices *)iod->private_data;
+ struct io_device *real_iod;
+
+ ch = raw_header->channel;
+ real_iod = io_raw_devs->raw_devices[ch];
+ if (!real_iod) {
+ pr_err("[MODEM_IF] %s: wrong channel %d\n", __func__, ch);
+ return -1;
+ }
+
+ *((struct io_device **)iod->skb_recv->cb) = real_iod;
+ skb_queue_tail(&iod->sk_rx_q, iod->skb_recv);
+ pr_debug("sk_rx_qlen:%d\n", iod->sk_rx_q.qlen);
+
+ schedule_delayed_work(&iod->rx_work, 0);
+ return 0;
+}
+
+/* de-mux function draft */
+static int rx_iodev_skb(struct io_device *iod)
+{
+ switch (iod->format) {
+ case IPC_MULTI_RAW:
+ return rx_multipdp(iod);
+
+ case IPC_FMT:
+ case IPC_RFS:
+ default:
+ skb_queue_tail(&iod->sk_rx_q, iod->skb_recv);
+
+ pr_debug("[MODEM_IF] wake up fmt,rfs skb\n");
+ wake_up(&iod->wq);
+ return 0;
+ }
+}
+
+static int rx_hdlc_packet(struct io_device *iod, const char *data,
+ unsigned recv_size)
+{
+ unsigned rest = recv_size;
+ char *buf = (char *)data;
+ int err = 0;
+ int len;
+ struct modem_data *md = (struct modem_data *)\
+ iod->mc->dev->platform_data;
+
+
+ if (rest <= 0)
+ goto exit;
+
+ pr_debug("[MODEM_IF] RX_SIZE=%d\n", rest);
+
+ if (iod->h_data.flag_len)
+ goto data_check;
+
+next_frame:
+ err = len = rx_hdlc_head_check(iod, buf, rest);
+ if (err < 0)
+ goto exit; /* buf++; rest--; goto next_frame; */
+ pr_debug("[MODEM_IF] check len : %d, rest : %d (%d)\n", len, rest,
+ __LINE__);
+
+ buf += len;
+ rest -= len;
+ if (rest <= 0)
+ goto exit;
+
+data_check:
+ err = len = rx_hdlc_data_check(iod, buf, rest);
+ if (err < 0)
+ goto exit;
+ pr_debug("[MODEM_IF] check len : %d, rest : %d (%d)\n", len, rest,
+ __LINE__);
+
+ /* If the lenght of actual data is odd. Skip the dummy bit*/
+ if (md->align == 1) {
+ if ((iod->format == IPC_RAW || iod->format == IPC_MULTI_RAW)
+ && (iod->net_typ == CDMA_NETWORK) && (len & 0x01))
+ len++;
+ }
+ buf += len;
+ rest -= len;
+
+ if (!rest && iod->h_data.flag_len)
+ return 0;
+ else if (rest <= 0)
+ goto exit;
+
+ err = len = rx_hdlc_tail_check(buf);
+ if (err < 0) {
+ pr_err("[MODEM_IF] Wrong HDLC end: 0x%x(%s)\n",
+ *buf, iod->name);
+ goto exit;
+ }
+ pr_debug("[MODEM_IF] check len : %d, rest : %d (%d)\n", len, rest,
+ __LINE__);
+
+ /* Skip the dummy byte inserted for 2-byte alignment in header.
+ Ox7E 00.*/
+ if (md->align == 1) {
+ if ((iod->format == IPC_RAW || iod->format == IPC_MULTI_RAW)
+ && (iod->net_typ == CDMA_NETWORK))
+ len++;
+ }
+ buf += len;
+ rest -= len;
+ if (rest < 0)
+ goto exit;
+
+ err = rx_iodev_skb(iod);
+ if (err < 0)
+ goto exit;
+
+ /* initialize header & skb */
+ iod->skb_recv = NULL;
+ memset(&iod->h_data, 0x00, sizeof(struct header_data));
+
+ if (rest)
+ goto next_frame;
+
+exit:
+ /* free buffers. mipi-hsi re-use recv buf */
+ if (rest < 0)
+ err = -ERANGE;
+
+ if (err < 0) {
+ /* clear headers */
+ memset(&iod->h_data, 0x00, sizeof(struct header_data));
+
+ if (iod->skb_recv) {
+ dev_kfree_skb_any(iod->skb_recv);
+ iod->skb_recv = NULL;
+ }
+ }
+
+ 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,
+ const char *data, unsigned int len)
+{
+ struct sk_buff *skb;
+ int err;
+
+ switch (iod->format) {
+ case IPC_FMT:
+ case IPC_RAW:
+ case IPC_RFS:
+ case IPC_MULTI_RAW:
+ err = rx_hdlc_packet(iod, data, len);
+ if (err < 0)
+ pr_err("[MODEM_IF] fail process hdlc fram\n");
+ return err;
+
+ case IPC_CMD:
+ /* TODO- handle flow control command from CP */
+ return 0;
+
+ case IPC_BOOT:
+ case IPC_RAMDUMP:
+ /* save packet to sk_buff */
+ skb = alloc_skb(len, GFP_ATOMIC);
+ if (!skb) {
+ pr_err("[MODEM_IF] fail alloc skb (%d)\n", __LINE__);
+ return -ENOMEM;
+ }
+
+ pr_debug("[MODEM_IF] boot len : %d\n", len);
+
+ memcpy(skb_put(skb, len), data, len);
+ skb_queue_tail(&iod->sk_rx_q, skb);
+ pr_debug("[MODEM_IF] skb len : %d\n", skb->len);
+
+ wake_up(&iod->wq);
+ return len;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+/* inform the IO device that the modem is now online or offline or
+ * crashing or whatever...
+ */
+static void io_dev_modem_state_changed(struct io_device *iod,
+ enum modem_state state)
+{
+ iod->mc->phone_state = state;
+ pr_info("[MODEM_IF] %s state changed: %s\n", \
+ iod->name, modem_state_name[state]);
+
+ if ((state == STATE_CRASH_EXIT) || (state == STATE_NV_REBUILDING))
+ wake_up(&iod->wq);
+}
+
+static int misc_open(struct inode *inode, struct file *filp)
+{
+ struct io_device *iod = to_io_device(filp->private_data);
+ filp->private_data = (void *)iod;
+
+ if (iod->format != IPC_BOOT && iod->format != IPC_RAMDUMP)
+ pr_info("[MODEM_IF] misc_open : %s\n", iod->name);
+
+ if (iod->link->init_comm)
+ return iod->link->init_comm(iod->link, iod);
+ return 0;
+}
+
+static int misc_release(struct inode *inode, struct file *filp)
+{
+ struct io_device *iod = (struct io_device *)filp->private_data;
+
+ if (iod->format != IPC_BOOT && iod->format != IPC_RAMDUMP)
+ pr_info("[MODEM_IF] misc_release : %s\n", iod->name);
+
+ if (iod->link->terminate_comm)
+ iod->link->terminate_comm(iod->link, iod);
+
+ skb_queue_purge(&iod->sk_rx_q);
+ return 0;
+}
+
+static unsigned int misc_poll(struct file *filp,
+ struct poll_table_struct *wait)
+{
+ struct io_device *iod = (struct io_device *)filp->private_data;
+
+ poll_wait(filp, &iod->wq, wait);
+
+ if ((!skb_queue_empty(&iod->sk_rx_q))
+ && (iod->mc->phone_state != STATE_OFFLINE))
+ return POLLIN | POLLRDNORM;
+ else if ((iod->mc->phone_state == STATE_CRASH_EXIT) ||
+ (iod->mc->phone_state == STATE_NV_REBUILDING))
+ return POLLHUP;
+ else
+ return 0;
+}
+
+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;
+
+ pr_debug("[MODEM_IF] misc_ioctl : 0x%x\n", cmd);
+
+ switch (cmd) {
+ case IOCTL_MODEM_ON:
+ pr_debug("[MODEM_IF] misc_ioctl : IOCTL_MODEM_ON\n");
+ return iod->mc->ops.modem_on(iod->mc);
+
+ case IOCTL_MODEM_OFF:
+ pr_debug("[MODEM_IF] misc_ioctl : IOCTL_MODEM_OFF\n");
+ return iod->mc->ops.modem_off(iod->mc);
+
+ case IOCTL_MODEM_RESET:
+ pr_debug("[MODEM_IF] misc_ioctl : IOCTL_MODEM_RESET\n");
+ return iod->mc->ops.modem_reset(iod->mc);
+
+ case IOCTL_MODEM_FORCE_CRASH_EXIT:
+ pr_debug("[MODEM_IF] misc_ioctl : IOCTL_MODEM_FORCE_CRASH_EXIT\n");
+ return iod->mc->ops.modem_force_crash_exit(iod->mc);
+
+ case IOCTL_MODEM_DUMP_RESET:
+ pr_debug("[MODEM_IF] misc_ioctl : IOCTL_MODEM_FORCE_CRASH_EXIT\n");
+ return iod->mc->ops.modem_dump_reset(iod->mc);
+
+ case IOCTL_MODEM_BOOT_ON:
+ pr_debug("[MODEM_IF] misc_ioctl : IOCTL_MODEM_BOOT_ON\n");
+ return iod->mc->ops.modem_boot_on(iod->mc);
+
+ case IOCTL_MODEM_BOOT_OFF:
+ pr_debug("[MODEM_IF] misc_ioctl : IOCTL_MODEM_BOOT_OFF\n");
+ return iod->mc->ops.modem_boot_off(iod->mc);
+
+ /* TODO - will remove this command after ril updated */
+ case IOCTL_MODEM_START:
+ pr_debug("[MODEM_IF] misc_ioctl : IOCTL_MODEM_START\n");
+ return 0;
+
+ case IOCTL_MODEM_STATUS:
+ pr_debug("[MODEM_IF] misc_ioctl : IOCTL_MODEM_STATUS\n");
+ p_state = iod->mc->phone_state;
+ if (p_state == STATE_NV_REBUILDING) {
+ pr_info("[MODEM_IF] nv rebuild state : %d\n", p_state);
+ iod->mc->phone_state = STATE_ONLINE;
+ }
+ return p_state;
+
+ case IOCTL_MODEM_DUMP_START:
+ pr_debug("[MODEM_IF] misc_ioctl : IOCTL_MODEM_DUMP_START\n");
+ return iod->link->dump_start(iod->link, iod);
+
+ case IOCTL_MODEM_DUMP_UPDATE:
+ pr_debug("[MODEM_IF] misc_ioctl : IOCTL_MODEM_DUMP_UPDATE\n");
+ return iod->link->dump_update(iod->link, iod, _arg);
+
+ case IOCTL_MODEM_GOTA_START:
+ pr_debug("[GOTA] misc_ioctl : IOCTL_MODEM_GOTA_START\n");
+ return iod->link->gota_start(iod->link, iod);
+
+ case IOCTL_MODEM_FW_UPDATE:
+ pr_debug("[GOTA] misc_ioctl : IOCTL_MODEM_FW_UPDATE\n");
+ return iod->link->modem_update(iod->link, iod, _arg);
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static ssize_t misc_write(struct file *filp, const char __user * buf,
+ size_t count, loff_t *ppos)
+{
+ struct io_device *iod = (struct io_device *)filp->private_data;
+ int frame_len = 0;
+ char frame_header_buf[sizeof(struct raw_hdr)];
+ struct sk_buff *skb;
+
+ /* TODO - check here flow control for only raw data */
+
+ if (iod->format == IPC_BOOT || iod->format == IPC_RAMDUMP)
+ frame_len = count + get_header_size(iod);
+ else
+ frame_len = count + SIZE_OF_HDLC_START + get_header_size(iod)
+ + SIZE_OF_HDLC_END;
+
+ skb = alloc_skb(frame_len, GFP_KERNEL);
+ if (!skb) {
+ pr_err("[MODEM_IF] fail alloc skb (%d)\n", __LINE__);
+ return -ENOMEM;
+ }
+
+ switch (iod->format) {
+ case IPC_BOOT:
+ case IPC_RAMDUMP:
+ if (copy_from_user(skb_put(skb, count), buf, count) != 0) {
+ dev_kfree_skb_any(skb);
+ return -EFAULT;
+ }
+ break;
+
+ case IPC_RFS:
+ memcpy(skb_put(skb, SIZE_OF_HDLC_START), hdlc_start,
+ SIZE_OF_HDLC_START);
+ if (copy_from_user(skb_put(skb, count), buf, count) != 0) {
+ dev_kfree_skb_any(skb);
+ return -EFAULT;
+ }
+ memcpy(skb_put(skb, SIZE_OF_HDLC_END), hdlc_end,
+ SIZE_OF_HDLC_END);
+ break;
+
+ default:
+ memcpy(skb_put(skb, SIZE_OF_HDLC_START), hdlc_start,
+ SIZE_OF_HDLC_START);
+ memcpy(skb_put(skb, get_header_size(iod)),
+ get_header(iod, count, frame_header_buf),
+ get_header_size(iod));
+ if (copy_from_user(skb_put(skb, count), buf, count) != 0) {
+ dev_kfree_skb_any(skb);
+ return -EFAULT;
+ }
+ memcpy(skb_put(skb, SIZE_OF_HDLC_END), hdlc_end,
+ SIZE_OF_HDLC_END);
+ break;
+ }
+
+ /* send data with sk_buff, link device will put sk_buff
+ * into the specific sk_buff_q and run work-q to send data
+ */
+ return iod->link->send(iod->link, iod, skb);
+}
+
+static ssize_t misc_read(struct file *filp, char *buf, size_t count,
+ loff_t *f_pos)
+{
+ struct io_device *iod = (struct io_device *)filp->private_data;
+ struct sk_buff *skb;
+ int pktsize = 0;
+
+ skb = skb_dequeue(&iod->sk_rx_q);
+ if (!skb) {
+ printk_ratelimited(KERN_ERR "[MODEM_IF] no data from sk_rx_q, "
+ "modem_state : %s(%s)\n",
+ modem_state_name[iod->mc->phone_state], iod->name);
+ return 0;
+ }
+
+ if (skb->len > count) {
+ pr_err("[MODEM_IF] skb len is too big = %d,%d!(%d)\n",
+ count, skb->len, __LINE__);
+ dev_kfree_skb_any(skb);
+ return -EIO;
+ }
+ pr_debug("[MODEM_IF] skb len : %d\n", skb->len);
+
+ pktsize = skb->len;
+ if (copy_to_user(buf, skb->data, pktsize) != 0)
+ return -EIO;
+ dev_kfree_skb_any(skb);
+
+ pr_debug("[MODEM_IF] copy to user : %d\n", pktsize);
+
+ return pktsize;
+}
+
+static const struct file_operations misc_io_fops = {
+ .owner = THIS_MODULE,
+ .open = misc_open,
+ .release = misc_release,
+ .poll = misc_poll,
+ .unlocked_ioctl = misc_ioctl,
+ .write = misc_write,
+ .read = misc_read,
+};
+
+static int vnet_open(struct net_device *ndev)
+{
+ netif_start_queue(ndev);
+ return 0;
+}
+
+static int vnet_stop(struct net_device *ndev)
+{
+ netif_stop_queue(ndev);
+ return 0;
+}
+
+static int vnet_xmit(struct sk_buff *skb, struct net_device *ndev)
+{
+ int ret;
+ struct raw_hdr hd;
+ struct sk_buff *skb_new;
+ struct vnet *vnet = netdev_priv(ndev);
+ struct io_device *iod = vnet->iod;
+
+ /* umts doesn't need to discard ethernet header */
+ if (iod->net_typ != UMTS_NETWORK) {
+ if (iod->id >= PSD_DATA_CHID_BEGIN &&
+ iod->id <= PSD_DATA_CHID_END)
+ skb_pull(skb, sizeof(struct ethhdr));
+ }
+
+ hd.len = skb->len + sizeof(hd);
+ hd.control = 0;
+ hd.channel = iod->id & 0x1F;
+
+ skb_new = skb_copy_expand(skb, sizeof(hd) + sizeof(hdlc_start),
+ sizeof(hdlc_end), GFP_ATOMIC);
+ if (!skb_new) {
+ dev_kfree_skb_any(skb);
+ return -ENOMEM;
+ }
+
+ memcpy(skb_push(skb_new, sizeof(hd)), &hd, sizeof(hd));
+ memcpy(skb_push(skb_new, sizeof(hdlc_start)), hdlc_start,
+ sizeof(hdlc_start));
+ memcpy(skb_put(skb_new, sizeof(hdlc_end)), hdlc_end, sizeof(hdlc_end));
+
+ ret = iod->link->send(iod->link, iod, skb_new);
+ if (ret < 0) {
+ dev_kfree_skb_any(skb);
+ return NETDEV_TX_BUSY;
+ }
+
+ ndev->stats.tx_packets++;
+ ndev->stats.tx_bytes += skb->len;
+ dev_kfree_skb_any(skb);
+
+ return NETDEV_TX_OK;
+}
+
+static struct net_device_ops vnet_ops = {
+ .ndo_open = vnet_open,
+ .ndo_stop = vnet_stop,
+ .ndo_start_xmit = vnet_xmit,
+};
+
+static void vnet_setup(struct net_device *ndev)
+{
+ ndev->netdev_ops = &vnet_ops;
+ ndev->type = ARPHRD_PPP;
+ ndev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST;
+ ndev->addr_len = 0;
+ ndev->hard_header_len = 0;
+ ndev->tx_queue_len = 1000;
+ ndev->mtu = ETH_DATA_LEN;
+ ndev->watchdog_timeo = 5 * HZ;
+}
+
+static void vnet_setup_ether(struct net_device *ndev)
+{
+ ndev->netdev_ops = &vnet_ops;
+ ndev->type = ARPHRD_ETHER;
+ ndev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST | IFF_SLAVE;
+ ndev->addr_len = ETH_ALEN;
+ random_ether_addr(ndev->dev_addr);
+ ndev->hard_header_len = 0;
+ ndev->tx_queue_len = 1000;
+ ndev->mtu = ETH_DATA_LEN;
+ ndev->watchdog_timeo = 5 * HZ;
+}
+
+int init_io_device(struct io_device *iod)
+{
+ int ret = 0;
+ struct vnet *vnet;
+
+ /* get modem state from modem control device */
+ iod->modem_state_changed = io_dev_modem_state_changed;
+ /* get data from link device */
+ iod->recv = io_dev_recv_data_from_link_dev;
+
+ INIT_LIST_HEAD(&iod->list);
+
+ /* register misc or net drv */
+ switch (iod->io_typ) {
+ case IODEV_MISC:
+ init_waitqueue_head(&iod->wq);
+ skb_queue_head_init(&iod->sk_rx_q);
+ INIT_DELAYED_WORK(&iod->rx_work, rx_iodev_work);
+
+ iod->miscdev.minor = MISC_DYNAMIC_MINOR;
+ iod->miscdev.name = iod->name;
+ iod->miscdev.fops = &misc_io_fops;
+
+ ret = misc_register(&iod->miscdev);
+ if (ret)
+ pr_err("failed to register misc io device : %s\n",
+ iod->name);
+
+ break;
+
+ case IODEV_NET:
+ if (iod->net_typ == UMTS_NETWORK)
+ iod->ndev = alloc_netdev(0, iod->name, vnet_setup);
+ else
+ iod->ndev = alloc_netdev(0, iod->name,
+ vnet_setup_ether);
+ if (!iod->ndev) {
+ pr_err("failed to alloc netdev\n");
+ return -ENOMEM;
+ }
+
+ ret = register_netdev(iod->ndev);
+ if (ret)
+ free_netdev(iod->ndev);
+
+ pr_debug("%s: %d(iod:0x%p)\n", __func__, __LINE__, iod);
+ vnet = netdev_priv(iod->ndev);
+ pr_debug("%s: %d(vnet:0x%p)\n", __func__, __LINE__, vnet);
+ vnet->iod = iod;
+
+ break;
+
+ case IODEV_DUMMY:
+ skb_queue_head_init(&iod->sk_rx_q);
+ INIT_DELAYED_WORK(&iod->rx_work, rx_iodev_work);
+
+ break;
+
+ default:
+ pr_err("wrong io_type : %d\n", iod->io_typ);
+ return -EINVAL;
+ }
+
+ pr_debug("[MODEM_IF] %s(%d) : init_io_device() done : %d\n",
+ iod->name, iod->io_typ, ret);
+ return ret;
+}
diff --git a/drivers/misc/modem_if_na/modem_link_device_dpram.c b/drivers/misc/modem_if_na/modem_link_device_dpram.c
new file mode 100644
index 0000000..bf4f61a
--- /dev/null
+++ b/drivers/misc/modem_if_na/modem_link_device_dpram.c
@@ -0,0 +1,1504 @@
+/*
+ * Copyright (C) 2011 Google, Inc.
+ * Copyright (C) 2010 Samsung Electronics.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/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/platform_data/modem_na.h>
+#include <linux/io.h>
+#include "modem_prj.h"
+#include "modem_link_device_dpram.h"
+
+/* interrupt masks.*/
+#define INT_MASK_VALID 0x0080
+#define INT_MASK_CMD 0x0040
+#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_VALID(x) ((x) & INT_MASK_VALID)
+#define INT_CMD_VALID(x) ((x) & INT_MASK_CMD)
+#define INT_NON_CMD(x) (INT_MASK_VALID | (x))
+#define INT_CMD(x) (INT_MASK_VALID | INT_MASK_CMD | (x))
+
+#define INT_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_PHONE_START 0x8
+#define INT_CMD_ERR_DISPLAY 0x9
+#define INT_CMD_PHONE_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
+
+/* special interrupt cmd indicating modem boot failure. */
+#define INT_POWERSAFE_FAIL 0xDEAD
+
+#define GOTA_CMD_VALID(x) (((x) & 0xA000) == 0xA000)
+#define GOTA_RESULT_FAIL 0x2
+#define GOTA_RESULT_SUCCESS 0x1
+#define GOTA_CMD_MASK(x) (((x) >> 8) & 0xF)
+#define GOTA_CMD_RECEIVE_READY 0x1
+#define GOTA_CMD_DOWNLOAD_START_REQ 0x2
+#define GOTA_CMD_DOWNLOAD_START_RESP 0x3
+#define GOTA_CMD_IMAGE_SEND_REQ 0x4
+#define GOTA_CMD_IMAGE_SEND_RESP 0x5
+#define GOTA_CMD_SEND_DONE_REQ 0x6
+#define GOTA_CMD_SEND_DONE_RESP 0x7
+#define GOTA_CMD_STATUS_UPDATE 0x8
+#define GOTA_CMD_UPDATE_DONE 0x9
+#define GOTA_CMD_EFS_CLEAR_RESP 0xB
+#define GOTA_CMD_ALARM_BOOT_OK 0xC
+#define GOTA_CMD_ALARM_BOOT_FAIL 0xD
+
+#define CMD_DL_START_REQ 0x9200
+#define CMD_IMG_SEND_REQ 0x9400
+#define CMD_DL_SEND_DONE_REQ 0x9600
+#define CMD_UL_RECEIVE_RESP 0x9601
+#define CMD_UL_RECEIVE_DONE_RESP 0x9801
+
+#define START_INDEX 0x7F
+#define END_INDEX 0x7E
+
+#define DP_MAGIC_CODE 0xAA
+#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 16366
+#ifdef CONFIG_INTERNAL_MODEM_IF
+#define DP_DUMP_HEADER_SIZE 8
+#else
+#define DP_DUMP_HEADER_SIZE 7
+#endif
+#define GOTA_TIMEOUT (50 * HZ)
+#define GOTA_SEND_TIMEOUT (200 * HZ)
+#define DUMP_TIMEOUT (30 * HZ)
+#define DUMP_START_TIMEOUT (100 * HZ)
+#define IDPRAM_PHY_START 0x13A00000
+#define IDPRAM_SIZE 0x4000
+
+
+
+static int
+dpram_download(struct dpram_link_device *dpld, const char *buf, int len);
+static int
+dpram_upload(struct dpram_link_device *dpld, struct dpram_firmware *uploaddata);
+static inline void
+dpram_writeh(u16 value, void __iomem *p_dest);
+static void
+dpram_clear(struct dpram_link_device *dpld);
+static struct io_device *
+dpram_find_iod(struct dpram_link_device *dpld, int id);
+static void
+dpram_write_command(struct dpram_link_device *dpld, u16 cmd);
+static inline int
+dpram_readh(void __iomem *p_dest);
+
+#ifdef CONFIG_INTERNAL_MODEM_IF
+
+#define INT_MASK_CMD_PDA_SLEEP 0x0C
+#define INT_MASK_CMD_DPRAM_DOWN 0x0C
+#define INT_MASK_CMD_PDA_WAKEUP 0x0A
+#define INT_MASK_CMD_CP_WAKEUP_START 0x0A
+#define INT_MASK_CMD_DPRAM_DOWN_NACK 0x07
+
+#include <plat/gpio-cfg.h>
+#include <linux/suspend.h>
+
+struct idpram_link_pm_data *pm;
+
+void idpram_magickey_init(struct idpram_link_pm_data *pm_data)
+{
+ u16 acc_code = 0x01;
+
+ dpram_writeh(DP_MAGIC_CODE, &pm_data->dpld->dpram->magic);
+ dpram_writeh(acc_code, &pm_data->dpld->dpram->enable);
+}
+
+int idpram_get_write_lock(struct idpram_link_pm_data *pm_data)
+{
+ return atomic_read(&pm_data->write_lock);
+}
+
+static int idpram_write_lock(struct idpram_link_pm_data *pm_data, int lock)
+{
+ int lock_value = 0;
+
+ mif_info("MIF: idpram write_lock(%d)\n", lock);
+
+ switch (lock) {
+ case 0: /* unlock */
+ if (atomic_read(&pm_data->write_lock))
+ lock_value = atomic_dec_return(&pm_data->write_lock);
+ if (lock_value)
+ mif_err("MIF: ipdram write unlock but lock value=%d\n",
+ lock_value);
+ break;
+ case 1: /* lock */
+ if (!atomic_read(&pm_data->write_lock))
+ lock_value = atomic_inc_return(&pm_data->write_lock);
+ if (lock_value != 1)
+ mif_err("MIF: ipdram write lock but lock value=%d\n",
+ lock_value);
+ break;
+ }
+ return 0;
+}
+
+static int idpram_resume_init(struct idpram_link_pm_data *pm_data)
+{
+
+ pm_data->pm_states = IDPRAM_PM_RESUME_START;
+ pm_data->last_pm_mailbox = 0;
+
+ dpram_clear(pm_data->dpld);
+ idpram_magickey_init(pm_data);
+
+ /* Initialize the dpram controller */
+ pm_data->mdata->sfr_init();
+
+ /*re-initialize internal dpram gpios */
+ s3c_gpio_cfgpin(pm_data->mdata->gpio_mbx_intr, S3C_GPIO_SFN(0x2));
+
+ idpram_write_lock(pm_data, 0);
+ return 0;
+}
+
+
+void idpram_timeout_handler(struct idpram_link_pm_data *pm_data)
+{
+ struct io_device *iod = dpram_find_iod(pm_data->dpld, FMT_IDX);
+
+ mif_info("MIF: <%s>", __func__);
+
+ if (!gpio_get_value(pm_data->mdata->gpio_phone_active)) {
+ mif_err("MIF: <%s:%s> (Crash silent Reset)\n",
+ __func__, pm_data->dpld->ld.name);
+
+ if (iod && iod->modem_state_changed)
+ iod->modem_state_changed(iod, STATE_CRASH_EXIT);
+ }
+}
+
+static int idpram_resume_check(struct idpram_link_pm_data *pm_data)
+{
+ /* check last pm mailbox */
+ mif_info("MIF: idpram %s, last_pm_mailbox=%x\n", __func__,
+ pm_data->last_pm_mailbox);
+
+ if (pm_data->last_pm_mailbox == INT_CMD(INT_MASK_CMD_PDA_WAKEUP)) {
+ pm_data->last_pm_mailbox = 0;
+ return 0;
+ }
+
+ dpram_write_command(pm_data->dpld, INT_CMD(INT_MASK_CMD_PDA_WAKEUP));
+ mif_info("MIF: idpram sent PDA_WAKEUP Mailbox(0x%x)\n",
+ INT_CMD(INT_MASK_CMD_PDA_WAKEUP));
+
+ return -1;
+}
+
+static void idpram_resume_retry(struct work_struct *work)
+{
+ struct idpram_link_pm_data *pm_data =
+ container_of(work, struct idpram_link_pm_data, \
+ resume_work.work);
+
+ mif_debug("MIF: %s\n", __func__);
+
+ if (!idpram_resume_check(pm_data)) {
+ mif_info("MIF: idpram resume ok\n");
+ idpram_write_lock(pm_data, 0);
+ wake_lock_timeout(&pm_data->hold_wlock, msecs_to_jiffies(20));
+ return;
+ }
+ if (pm_data->resume_retry--) {
+ schedule_delayed_work(&pm_data->resume_work, \
+ msecs_to_jiffies(200));
+ wake_lock_timeout(&pm_data->hold_wlock, msecs_to_jiffies(260));
+ } else {
+ mif_info("MIF: idpram resume T-I-M-E-O-UT\n");
+ idpram_timeout_handler(pm_data);
+ /* hold wakelock until uevnet sent to rild */
+ wake_lock_timeout(&pm_data->hold_wlock, HZ*7);
+ idpram_write_lock(pm_data, 0);
+ }
+}
+
+
+static irqreturn_t link_ap_wakeup_handler(int irq, void *data)
+{
+ struct idpram_link_pm_data *pm_data = data;
+
+ mif_info("MIF: <%s> 5 seconds.\n", __func__);
+ wake_lock_timeout(&pm_data->host_wakeup_wlock, 5*HZ);
+
+ return IRQ_HANDLED;
+}
+
+static int idpram_pm_suspend(struct device *dev)
+{
+ struct idpram_link_pm_data *pm_data = pm;
+
+ pm_data->pm_states = IDPRAM_PM_SUSPEND_START;
+ gpio_set_value(pm_data->mdata->gpio_pda_active, 0);
+
+ mif_debug("MIF: <%s>\n", __func__);
+
+ return 0;
+}
+static int idpram_pm_resume(struct device *dev)
+{
+ struct idpram_link_pm_data *pm_data = pm;
+
+ idpram_resume_init(pm_data);
+ gpio_set_value(pm_data->mdata->gpio_pda_active, 1);
+ mif_debug("MIF: <%s>\n", __func__);
+ return 0;
+}
+
+static int __devinit idpram_pm_probe(struct platform_device *pdev)
+{
+ return 0;
+}
+static void idpram_pm_shutdown(struct platform_device *pdev)
+{
+}
+
+static const struct dev_pm_ops idpram_pm_ops = {
+ .suspend = idpram_pm_suspend,
+ .resume = idpram_pm_resume,
+};
+
+static struct platform_driver idpram_pm_driver = {
+ .probe = idpram_pm_probe,
+ .shutdown = idpram_pm_shutdown,
+ .driver = {
+ .name = "idparam_pm",
+ .pm = &idpram_pm_ops,
+ },
+};
+
+static void idpram_powerup_start(struct idpram_link_pm_data *pm_data)
+{
+ pm_data->last_pm_mailbox = INT_CMD(INT_MASK_CMD_PDA_WAKEUP);
+ pm_data->pm_states = IDPRAM_PM_ACTIVE;
+ mif_debug("MIF: <%s>\n", __func__);
+}
+
+static void idpram_power_down_nack(struct idpram_link_pm_data *pm_data)
+{
+ pm_data->last_pm_mailbox = INT_CMD(INT_MASK_CMD_DPRAM_DOWN_NACK);
+ complete(&pm_data->idpram_down);
+ mif_debug("MIF: <%s>\n", __func__);
+}
+
+static void idpram_power_down(struct idpram_link_pm_data *pm_data)
+{
+ pm_data->last_pm_mailbox = INT_CMD(INT_MASK_CMD_DPRAM_DOWN);
+ complete(&pm_data->idpram_down);
+ mif_debug("MIF: <%s>\n", __func__);
+}
+
+static int idpram_post_resume(struct idpram_link_pm_data *pm_data)
+{
+ int gpio_val = 0;
+
+ mif_info("MIF: idpram %s\n", __func__);
+
+ switch (pm_data->pm_states) {
+ /* schedule_work */
+ case IDPRAM_PM_DPRAM_POWER_DOWN:
+ gpio_set_value(pm_data->mdata->gpio_pda_active, 0);
+ mif_info("MIF: idpram PDA_ACTIVE LOW\n");
+
+ msleep(50);
+
+ idpram_resume_init(pm_data);
+
+ msleep(50);
+
+ gpio_set_value(pm_data->mdata->gpio_pda_active, 1);
+
+ msleep(20);
+
+ gpio_val = gpio_get_value(pm_data->mdata->gpio_pda_active);
+ mif_info("MIF: idpram PDA_ACTIVE (%d)\n", gpio_val);
+
+ if (gpio_val == 0) {
+ gpio_set_value(pm_data->mdata->gpio_pda_active, 1);
+ mif_info("MIF: idpram PDA_ACTIVE set again.\n");
+ }
+ break;
+
+ case IDPRAM_PM_RESUME_START:
+ break;
+
+ case IDPRAM_PM_SUSPEND_PREPARE:
+ break;
+ }
+ return 0;
+}
+
+
+static int idpram_pre_suspend(struct idpram_link_pm_data *pm_data)
+{
+ int timeout_ret = 0;
+ int suspend_retry = 2;
+ u16 intr_out = INT_CMD(INT_MASK_CMD_PDA_SLEEP);
+
+ pm_data->pm_states = IDPRAM_PM_SUSPEND_PREPARE;
+ pm_data->last_pm_mailbox = 0;
+ idpram_write_lock(pm_data, 1);
+
+ gpio_set_value(pm_data->mdata->gpio_mbx_intr, 1);
+
+ /* prevent PDA_ACTIVE ststus is low */
+ gpio_set_value(pm_data->mdata->gpio_pda_active, 1);
+
+ if (!atomic_read(&pm_data->read_lock)) {
+ do {
+ init_completion(&pm_data->idpram_down);
+ dpram_write_command(pm_data->dpld, intr_out);
+ mif_err("MIF: idpram sent PDA_SLEEP Mailbox(0x%X)\n",
+ intr_out);
+ timeout_ret =
+ wait_for_completion_timeout(&pm_data->idpram_down,
+ (HZ/5));
+ mif_err("MIF: suspend_enter cnt = %d\n",
+ suspend_retry);
+ } while (!timeout_ret && suspend_retry--);
+
+ switch (pm_data->last_pm_mailbox) {
+ case INT_CMD(INT_MASK_CMD_DPRAM_DOWN):
+ break;
+
+ /* if nack or other interrup, hold wakelock for DPM resume*/
+ case INT_CMD(INT_MASK_CMD_DPRAM_DOWN_NACK):
+ mif_err("MIF: idpram dpram down get NACK\n");
+
+ default:
+ mif_err("MIF: CP dpram Down not ready! intr=0x%X\n",
+ dpram_readh(&pm_data->dpld->dpram->mbx_cp2ap));
+ wake_lock_timeout(&pm_data->hold_wlock,
+ msecs_to_jiffies(500));
+ idpram_write_lock(pm_data, 0);
+ return 0;
+ }
+ /*
+ * 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(pm_data->mdata->gpio_mbx_intr, 1);
+ s3c_gpio_cfgpin(pm_data->mdata->gpio_mbx_intr, S3C_GPIO_OUTPUT);
+ pm_data->pm_states = IDPRAM_PM_DPRAM_POWER_DOWN;
+
+ return 0;
+ } else {
+ mif_err("MIF: idpram hold read_lock\n");
+ return -EBUSY;
+ }
+}
+
+static int idpram_notifier_event(struct notifier_block *this,
+ unsigned long event, void *ptr)
+{
+ int err;
+
+ switch (event) {
+ case PM_SUSPEND_PREPARE:
+ mif_debug("MIF: PM_SUSPEND_PREPARE+\n");
+ err = idpram_pre_suspend(pm);
+ if (err)
+ mif_err("MIF: pre-suspend err\n");
+ break;
+
+ case PM_POST_SUSPEND:
+ mif_debug("MIF: PM_POST_SUSPEND+\n");
+ err = idpram_post_resume(pm);
+ if (err)
+ mif_err("MIF: pre-suspend err\n");
+ break;
+ }
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block idpram_link_pm_notifier = {
+ .notifier_call = idpram_notifier_event,
+};
+
+static int idpram_link_pm_init
+(
+struct dpram_link_device *idpram_ld,
+struct platform_device *pdev
+)
+{
+ int r = 0;
+ unsigned irq = 0;
+ struct modem_data *pdata =
+ (struct modem_data *)pdev->dev.platform_data;
+
+ mif_info("MIF: <%s>\n", __func__);
+
+ pm = kzalloc(sizeof(struct idpram_link_pm_data), GFP_KERNEL);
+ if (!pm) {
+ mif_err("MIF: %s: link_pm_data is NULL\n", __func__);
+ return -ENOMEM;
+ }
+
+ pm->mdata = pdata;
+ pm->dpld = idpram_ld;
+ idpram_ld->link_pm_data = pm;
+
+ /*for pm notifire*/
+ register_pm_notifier(&idpram_link_pm_notifier);
+
+
+ init_completion(&pm->idpram_down);
+ wake_lock_init(&pm->host_wakeup_wlock,
+ WAKE_LOCK_SUSPEND,
+ "HOST_WAKEUP_WLOCK");
+ wake_lock_init(&pm->rd_wlock, WAKE_LOCK_SUSPEND, "dpram_pwrdn");
+ wake_lock_init(&pm->hold_wlock, WAKE_LOCK_SUSPEND, "dpram_hold");
+ wake_lock_init(&pm->wakeup_wlock, WAKE_LOCK_SUSPEND, "dpram_wakeup");
+ atomic_set(&pm->read_lock, 0);
+ atomic_set(&pm->write_lock, 0);
+ INIT_DELAYED_WORK(&pm->resume_work, idpram_resume_retry);
+
+ r = platform_driver_register(&idpram_pm_driver);
+ if (r) {
+ mif_err("MIF: wakelocks_init: platform_driver_register failed\n");
+ goto err_platform_driver_register;
+ }
+
+ irq = gpio_to_irq(pdata->gpio_ap_wakeup);
+ r = request_irq(irq, link_ap_wakeup_handler,
+ IRQF_TRIGGER_RISING,
+ "idpram_host_wakeup", (void *)pm);
+
+ mif_info("MIF: <%s> DPRAM IRQ# = %d, %d\n", __func__,
+ irq,
+ pm->mdata->gpio_ap_wakeup);
+
+ if (r) {
+ mif_err("MIF: %s:fail to request irq(%d) host_wake_irq\n",
+ __func__, r);
+ goto err_request_irq;
+ }
+
+ r = enable_irq_wake(irq);
+ if (r) {
+ mif_err("MIF: %s: failed to enable_irq_wake:%d host_wake_irq\n",
+ __func__, r);
+ goto err_set_wake_irq;
+ }
+
+ mif_info("MIF: <%s> END\n", __func__);
+ return 0;
+
+err_set_wake_irq:
+ free_irq(irq, (void *)pm);
+err_request_irq:
+ platform_driver_unregister(&idpram_pm_driver);
+err_platform_driver_register:
+ kfree(pm);
+ return r;
+}
+#endif /*CONFIG_INTERNAL_MODEM_IF*/
+
+
+
+static inline int dpram_readh(void __iomem *p_dest)
+{
+ unsigned long dest = (unsigned long)p_dest;
+ return ioread16(dest);
+}
+
+static inline void dpram_writew(u32 value, void __iomem *p_dest)
+{
+ unsigned long dest = (unsigned long)p_dest;
+ iowrite32(value, dest);
+}
+
+static inline void dpram_writeh(u16 value, void __iomem *p_dest)
+{
+ unsigned long dest = (unsigned long)p_dest;
+ iowrite16(value, dest);
+}
+
+static inline void dpram_writeb(u8 value, void __iomem *p_dest)
+{
+ unsigned long dest = (unsigned long)p_dest;
+ iowrite8(value, dest);
+}
+
+
+static void dpram_write_command(struct dpram_link_device *dpld, u16 cmd)
+{
+ dpram_writeh(cmd, &dpld->dpram->mbx_ap2cp);
+}
+
+static void dpram_clear_interrupt(struct dpram_link_device *dpld)
+{
+ dpram_writeh(0, &dpld->dpram->mbx_cp2ap);
+}
+
+static void dpram_drop_data(struct dpram_device *device, u16 head)
+{
+ dpram_writeh(head, &device->in->tail);
+}
+
+static void dpram_zero_circ(struct dpram_circ *circ)
+{
+ dpram_writeh(0, &circ->head);
+ dpram_writeh(0, &circ->tail);
+}
+
+static void dpram_clear(struct dpram_link_device *dpld)
+{
+ dpram_zero_circ(&dpld->dpram->fmt_out);
+ dpram_zero_circ(&dpld->dpram->raw_out);
+ dpram_zero_circ(&dpld->dpram->fmt_in);
+ dpram_zero_circ(&dpld->dpram->raw_in);
+}
+
+static bool dpram_circ_valid(int size, u16 head, u16 tail)
+{
+ if (head >= size) {
+ mif_err("MIF: head(%d) >= size(%d)\n", head, size);
+ return false;
+ }
+ if (tail >= size) {
+ mif_err("MIF: tail(%d) >= size(%d)\n", tail, size);
+ return false;
+ }
+ return true;
+}
+
+static int dpram_init_and_report(struct dpram_link_device *dpld)
+{
+ const u16 init_end = INT_CMD(INT_CMD_INIT_END);
+ u16 magic;
+ u16 enable;
+
+ dpram_writeh(0, &dpld->dpram->enable);
+ dpram_clear(dpld);
+ dpram_writeh(DP_MAGIC_CODE, &dpld->dpram->magic);
+ dpram_writeh(1, &dpld->dpram->enable);
+
+ /* Send init end code to modem */
+ dpram_write_command(dpld, init_end);
+
+ magic = dpram_readh(&dpld->dpram->magic);
+ if (magic != DP_MAGIC_CODE) {
+ mif_err("MIF:: %s: Failed to check magic\n", __func__);
+ return -1;
+ }
+
+ enable = dpram_readh(&dpld->dpram->enable);
+ if (!enable) {
+ mif_err("MIF:: %s: DPRAM enable failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+static struct io_device *dpram_find_iod(struct dpram_link_device *dpld, int id)
+{
+ struct io_device *iod;
+
+ list_for_each_entry(iod, &dpld->list_of_io_devices, list) {
+ if ((id == FMT_IDX && iod->format == IPC_FMT) ||
+ (id == RAW_IDX && iod->format == IPC_MULTI_RAW))
+ return iod;
+ }
+
+ return NULL;
+}
+
+static void cmd_req_active_handler(struct dpram_link_device *dpld)
+{
+ dpram_write_command(dpld, INT_CMD(INT_CMD_RES_ACTIVE));
+}
+
+static void cmd_error_display_handler(struct dpram_link_device *dpld)
+{
+ struct io_device *iod = dpram_find_iod(dpld, FMT_IDX);
+
+ mif_info("MIF: Received 0xc9 from modem (CP Crash)\n");
+ mif_info("MIF: %s\n", dpld->dpram->fmt_in_buff);
+
+ if (iod && iod->modem_state_changed)
+ iod->modem_state_changed(iod, STATE_CRASH_EXIT);
+}
+
+static void cmd_phone_start_handler(struct dpram_link_device *dpld)
+{
+ mif_debug("MIF: Received 0xc8 from modem (Boot OK)\n");
+ complete_all(&dpld->dpram_init_cmd);
+ dpram_init_and_report(dpld);
+}
+
+static void cmd_nv_rebuild_handler(struct dpram_link_device *dpld)
+{
+ struct io_device *iod = dpram_find_iod(dpld, FMT_IDX);
+
+ mif_info("MIF: Received nv rebuilding from modem\n");
+ mif_info("MIF: %s\n", dpld->dpram->fmt_in_buff);
+
+ if (iod && iod->modem_state_changed)
+ iod->modem_state_changed(iod, STATE_NV_REBUILDING);
+}
+
+
+static void command_handler(struct dpram_link_device *dpld, u16 cmd)
+{
+ mif_debug("MIF: %s: %x\n", __func__, cmd);
+
+ switch (INT_CMD_MASK(cmd)) {
+ case INT_CMD_REQ_ACTIVE:
+ cmd_req_active_handler(dpld);
+ break;
+
+ case INT_CMD_ERR_DISPLAY:
+ cmd_error_display_handler(dpld);
+ break;
+
+ case INT_CMD_PHONE_START:
+ cmd_phone_start_handler(dpld);
+ break;
+
+ case INT_CMD_NV_REBUILDING:
+ mif_err("[MODEM_IF] NV_REBUILDING\n");
+ cmd_nv_rebuild_handler(dpld);
+ break;
+
+ case INT_CMD_PIF_INIT_DONE:
+ complete_all(&dpld->modem_pif_init_done);
+ break;
+
+ case INT_CMD_SILENT_NV_REBUILDING:
+ mif_err("[MODEM_IF] SILENT_NV_REBUILDING\n");
+ break;
+
+#ifdef CONFIG_INTERNAL_MODEM_IF
+ case INT_MASK_CMD_DPRAM_DOWN:
+ idpram_power_down(dpld->link_pm_data);
+ break;
+
+ case INT_MASK_CMD_DPRAM_DOWN_NACK:
+ idpram_power_down_nack(dpld->link_pm_data);
+ break;
+
+ case INT_MASK_CMD_CP_WAKEUP_START:
+ idpram_powerup_start(dpld->link_pm_data);
+ break;
+#else
+ case INT_CMD_NORMAL_POWER_OFF:
+ /*ToDo:*/
+ /*kernel_sec_set_cp_ack()*/;
+ break;
+
+ case INT_CMD_REQ_TIME_SYNC:
+ case INT_CMD_PHONE_DEEP_SLEEP:
+ case INT_CMD_EMER_DOWN:
+ break;
+#endif
+
+ default:
+ mif_err("Unknown command.. %x\n", cmd);
+ }
+}
+
+
+static int dpram_process_modem_update(struct dpram_link_device *dpld,
+ struct dpram_firmware *pfw)
+{
+ int ret = 0;
+ char *buff = vmalloc(pfw->size);
+
+ mif_debug("[GOTA] modem size =[%d]\n", pfw->size);
+
+ if (!buff)
+ return -ENOMEM;
+
+ ret = copy_from_user(buff, pfw->firmware, pfw->size);
+ if (ret < 0) {
+ mif_err("[%s:%d] Copy from user failed\n", __func__, __LINE__);
+ goto out;
+ }
+
+ ret = dpram_download(dpld, buff, pfw->size);
+ if (ret < 0)
+ mif_err("firmware write failed\n");
+
+out:
+ vfree(buff);
+ return ret;
+}
+
+
+static int dpram_modem_update(struct link_device *ld, struct io_device *iod,
+ unsigned long _arg)
+{
+ int ret;
+ struct dpram_link_device *dpld = to_dpram_link_device(ld);
+ struct dpram_firmware fw;
+
+ mif_debug("[GOTA] dpram_modem_update\n");
+
+ ret = copy_from_user(&fw, (void __user *)_arg, sizeof(fw));
+ if (ret < 0) {
+ mif_err("copy from user failed!");
+ return ret;
+ }
+
+ return dpram_process_modem_update(dpld, &fw);
+}
+
+static int dpram_dump_update(struct link_device *ld, struct io_device *iod,
+ unsigned long _arg)
+{
+ struct dpram_link_device *dpld = to_dpram_link_device(ld);
+ struct dpram_firmware *fw = (struct dpram_firmware *)_arg ;
+
+ mif_debug("MIF: dpram_dump_update()\n");
+
+ return dpram_upload(dpld, fw);
+}
+
+static int dpram_read(struct dpram_link_device *dpld,
+ struct dpram_device *device, int dev_idx)
+{
+ struct io_device *iod;
+ int size;
+ int tmp_size;
+ u16 head, tail;
+ char *buff;
+
+ head = dpram_readh(&device->in->head);
+ tail = dpram_readh(&device->in->tail);
+ mif_debug("=====> %s, head: %d, tail: %d\n", __func__, head, tail);
+
+ if (head == tail) {
+ mif_err("MIF: %s: head == tail\n", __func__);
+ goto err_dpram_read;
+ }
+
+ if (!dpram_circ_valid(device->in_buff_size, head, tail)) {
+ mif_err("MIF: %s: invalid circular buffer\n", __func__);
+ dpram_zero_circ(device->in);
+ goto err_dpram_read;
+ }
+
+ iod = dpram_find_iod(dpld, dev_idx);
+ if (!iod) {
+ mif_err("MIF: iod == NULL\n");
+ goto err_dpram_read;
+ }
+
+ /* Get data size in DPRAM*/
+ size = (head > tail) ? (head - tail) :
+ (device->in_buff_size - tail + head);
+
+ /* ----- (tail) 7f 00 00 7e (head) ----- */
+ if (head > tail) {
+ buff = device->in_buff_addr + tail;
+ if (iod->recv(iod, buff, size) < 0) {
+ mif_err("MIF: %s: recv error, dropping data\n",
+ __func__);
+ dpram_drop_data(device, head);
+ goto err_dpram_read;
+ }
+ } else { /* 00 7e (head) ----------- (tail) 7f 00 */
+ /* 1. tail -> buffer end.*/
+ tmp_size = device->in_buff_size - tail;
+ buff = device->in_buff_addr + tail;
+ if (iod->recv(iod, buff, tmp_size) < 0) {
+ mif_err("MIF: %s: recv error, dropping data\n",
+ __func__);
+ dpram_drop_data(device, head);
+ goto err_dpram_read;
+ }
+
+ /* 2. buffer start -> head.*/
+ if (size > tmp_size) {
+ buff = (char *)device->in_buff_addr;
+ if (iod->recv(iod, buff, (size - tmp_size)) < 0) {
+ mif_err("MIF: %s: recv error, dropping data\n",
+ __func__);
+ dpram_drop_data(device, head);
+ goto err_dpram_read;
+ }
+ }
+ }
+
+ /* new tail */
+ tail = (u16)((tail + size) % device->in_buff_size);
+ dpram_writeh(tail, &device->in->tail);
+
+ return size;
+
+err_dpram_read:
+ return -EINVAL;
+}
+
+static void non_command_handler(struct dpram_link_device *dpld,
+ u16 non_cmd)
+{
+ struct dpram_device *device = NULL;
+ u16 head, tail;
+ u16 magic, access;
+ int ret = 0;
+
+ mif_debug("MIF: Entering non_command_handler(0x%04X)\n", non_cmd);
+
+ magic = dpram_readh(&dpld->dpram->magic);
+ access = dpram_readh(&dpld->dpram->enable);
+
+ if (!access || magic != DP_MAGIC_CODE) {
+ mif_err("fmr recevie error!!!! access = 0x%x, magic =0x%x",
+ access, magic);
+ return;
+ }
+
+ /* Check formatted data region */
+ device = &dpld->dev_map[FMT_IDX];
+ head = dpram_readh(&device->in->head);
+ tail = dpram_readh(&device->in->tail);
+
+ if (!dpram_circ_valid(device->in_buff_size, head, tail)) {
+ mif_err("MIF: %s: invalid circular buffer\n", __func__);
+ dpram_zero_circ(device->in);
+ return;
+ }
+
+ if (head != tail) {
+ if (non_cmd & INT_MASK_REQ_ACK_F)
+ atomic_inc(&dpld->fmt_txq_req_ack_rcvd);
+
+ ret = dpram_read(dpld, device, FMT_IDX);
+ if (ret < 0)
+ mif_err("%s, dpram_read failed\n", __func__);
+
+ if (atomic_read(&dpld->fmt_txq_req_ack_rcvd) > 0) {
+ dpram_write_command(dpld,
+ INT_NON_CMD(INT_MASK_RES_ACK_F));
+ atomic_set(&dpld->fmt_txq_req_ack_rcvd, 0);
+ }
+ } else {
+ if (non_cmd & INT_MASK_REQ_ACK_F) {
+ dpram_write_command(dpld,
+ INT_NON_CMD(INT_MASK_RES_ACK_F));
+ atomic_set(&dpld->fmt_txq_req_ack_rcvd, 0);
+ }
+ }
+
+ /* Check raw data region */
+ device = &dpld->dev_map[RAW_IDX];
+ head = dpram_readh(&device->in->head);
+ tail = dpram_readh(&device->in->tail);
+
+ if (!dpram_circ_valid(device->in_buff_size, head, tail)) {
+ mif_err("MIF: %s: invalid circular buffer\n", __func__);
+ dpram_zero_circ(device->in);
+ return;
+ }
+
+ if (head != tail) {
+ if (non_cmd & INT_MASK_REQ_ACK_R)
+ atomic_inc(&dpld->raw_txq_req_ack_rcvd);
+
+ ret = dpram_read(dpld, device, RAW_IDX);
+ if (ret < 0)
+ mif_err("%s, dpram_read failed\n", __func__);
+
+ if (atomic_read(&dpld->raw_txq_req_ack_rcvd) > 0) {
+ dpram_write_command(dpld,
+ INT_NON_CMD(INT_MASK_RES_ACK_R));
+ atomic_set(&dpld->raw_txq_req_ack_rcvd, 0);
+ }
+ } else {
+ if (non_cmd & INT_MASK_REQ_ACK_R) {
+ dpram_write_command(dpld,
+ INT_NON_CMD(INT_MASK_RES_ACK_R));
+ atomic_set(&dpld->raw_txq_req_ack_rcvd, 0);
+ }
+ }
+}
+
+static void gota_cmd_handler(struct dpram_link_device *dpld, u16 cmd)
+{
+ if (cmd & GOTA_RESULT_FAIL) {
+ mif_err("[GOTA] Command failed: %04x\n", cmd);
+ return;
+ }
+
+ switch (GOTA_CMD_MASK(cmd)) {
+ case GOTA_CMD_RECEIVE_READY:
+ mif_debug("[GOTA] Send CP-->AP RECEIVE_READY\n");
+ dpram_write_command(dpld, CMD_DL_START_REQ);
+ break;
+
+ case GOTA_CMD_DOWNLOAD_START_RESP:
+ mif_debug("[GOTA] Send CP-->AP DOWNLOAD_START_RESP\n");
+ complete_all(&dpld->gota_download_start_complete);
+ break;
+
+ case GOTA_CMD_SEND_DONE_RESP:
+ mif_debug("[GOTA] Send CP-->AP SEND_DONE_RESP\n");
+ complete_all(&dpld->gota_send_done);
+ break;
+
+ case GOTA_CMD_UPDATE_DONE:
+ mif_debug("[GOTA] Send CP-->AP UPDATE_DONE\n");
+ complete_all(&dpld->gota_update_done);
+ break;
+
+ case GOTA_CMD_IMAGE_SEND_RESP:
+ mif_debug("MIF: Send CP-->AP IMAGE_SEND_RESP\n");
+ complete_all(&dpld->dump_receive_done);
+ break;
+
+ default:
+ mif_err("[GOTA] Unknown command.. %x\n", cmd);
+ }
+}
+
+static irqreturn_t dpram_irq_handler(int irq, void *p_ld)
+{
+ u16 cp2ap;
+
+ struct link_device *ld = (struct link_device *)p_ld;
+ struct dpram_link_device *dpld = to_dpram_link_device(ld);
+
+ cp2ap = dpram_readh(&dpld->dpram->mbx_cp2ap);
+#if 0
+ mif_err("MIF: received CP2AP = 0x%x\n", cp2ap);
+#endif
+ if (cp2ap == INT_POWERSAFE_FAIL) {
+ mif_err("MIF: Received POWERSAFE_FAIL\n");
+ goto exit_irq;
+ }
+
+ if (GOTA_CMD_VALID(cp2ap))
+ gota_cmd_handler(dpld, cp2ap);
+ else if (INT_CMD_VALID(cp2ap))
+ command_handler(dpld, cp2ap);
+ else if (INT_VALID(cp2ap))
+ non_command_handler(dpld, cp2ap);
+ else
+ mif_err("MIF: Invalid command %04x\n", cp2ap);
+
+exit_irq:
+#ifdef CONFIG_INTERNAL_MODEM_IF
+ dpld->clear_interrupt();
+#endif
+ return IRQ_HANDLED;
+}
+
+static int dpram_attach_io_dev(struct link_device *ld, struct io_device *iod)
+{
+ struct dpram_link_device *dpld = to_dpram_link_device(ld);
+
+ iod->link = ld;
+ /* list up io devices */
+ list_add(&iod->list, &dpld->list_of_io_devices);
+
+ return 0;
+}
+
+static int dpram_write(struct dpram_link_device *dpld,
+ struct dpram_device *device,
+ const unsigned char *buf,
+ int len)
+{
+ u16 head;
+ u16 tail;
+ u16 irq_mask;
+ int free_space;
+ int last_size;
+
+#ifdef CONFIG_INTERNAL_MODEM_IF
+ /* Internal DPRAM, check dpram ready?*/
+ if (idpram_get_write_lock(dpld->link_pm_data)) {
+ printk(KERN_INFO "dpram_write_net - not ready return -EAGAIN\n");
+ return -EAGAIN;
+ }
+#endif
+
+ head = dpram_readh(&device->out->head);
+ tail = dpram_readh(&device->out->tail);
+
+ if (!dpram_circ_valid(device->out_buff_size, head, tail)) {
+ mif_err("MIF: %s: invalid circular buffer\n", __func__);
+ dpram_zero_circ(device->out);
+ return -EINVAL;
+ }
+
+ free_space = (head < tail) ? tail - head - 1 :
+ device->out_buff_size + tail - head - 1;
+ if (len > free_space) {
+ mif_debug("WRITE: No space in Q\n"
+ "len[%d] free_space[%d] head[%u] tail[%u] out_buff_size =%d\n",
+ len, free_space, head, tail, device->out_buff_size);
+ return -EINVAL;
+ }
+
+ mif_debug("WRITE: len[%d] free_space[%d] head[%u] tail[%u] out_buff_size =%d\n",
+ len, free_space, head, tail, device->out_buff_size);
+
+ if (head < tail) {
+ /* +++++++++ head ---------- tail ++++++++++ */
+ memcpy((device->out_buff_addr + head), buf, len);
+ } else {
+ /* ------ tail +++++++++++ head ------------ */
+ last_size = device->out_buff_size - head;
+ memcpy((device->out_buff_addr + head), buf,
+ len > last_size ? last_size : len);
+ if (len > last_size) {
+ memcpy(device->out_buff_addr, (buf + last_size),
+ (len - last_size));
+ }
+ }
+
+ /* Update new head */
+ head = (u16)((head + len) % device->out_buff_size);
+ dpram_writeh(head, &device->out->head);
+
+ irq_mask = INT_MASK_VALID;
+
+ if (len > 0)
+ irq_mask |= device->mask_send;
+
+ dpram_write_command(dpld, irq_mask);
+
+ return len;
+}
+
+static void dpram_write_work(struct work_struct *work)
+{
+ struct link_device *ld =
+ container_of(work, struct link_device, tx_delayed_work.work);
+ struct dpram_link_device *dpld = to_dpram_link_device(ld);
+ struct dpram_device *device;
+ struct sk_buff *skb;
+ bool reschedule = false;
+ int ret;
+
+ device = &dpld->dev_map[FMT_IDX];
+ while ((skb = skb_dequeue(&ld->sk_fmt_tx_q))) {
+ ret = dpram_write(dpld, device, skb->data, skb->len);
+ if (ret < 0) {
+ skb_queue_head(&ld->sk_fmt_tx_q, skb);
+ reschedule = true;
+ break;
+ }
+ dev_kfree_skb_any(skb);
+ }
+
+ device = &dpld->dev_map[RAW_IDX];
+ while ((skb = skb_dequeue(&ld->sk_raw_tx_q))) {
+ ret = dpram_write(dpld, device, skb->data, skb->len);
+ if (ret < 0) {
+ skb_queue_head(&ld->sk_raw_tx_q, skb);
+ reschedule = true;
+ break;
+ }
+ dev_kfree_skb_any(skb);
+ }
+
+ if (reschedule)
+ schedule_delayed_work(&ld->tx_delayed_work,
+ msecs_to_jiffies(10));
+}
+
+static int dpram_send(struct link_device *ld, struct io_device *iod,
+ struct sk_buff *skb)
+{
+ int len = skb->len;
+ mif_debug("%s: iod->format = %d\n", __func__, iod->format);
+
+ switch (iod->format) {
+ case IPC_FMT:
+ skb_queue_tail(&ld->sk_fmt_tx_q, skb);
+ break;
+
+ case IPC_RAW:
+ skb_queue_tail(&ld->sk_raw_tx_q, skb);
+ break;
+
+ case IPC_BOOT:
+ case IPC_RFS:
+ default:
+ dev_kfree_skb_any(skb);
+ return 0;
+ }
+
+ schedule_delayed_work(&ld->tx_delayed_work, 0);
+ return len;
+}
+
+static void dpram_table_init(struct dpram_link_device *dpld)
+{
+ struct dpram_device *dev;
+ struct dpram_map __iomem *dpram = dpld->dpram;
+
+ dev = &dpld->dev_map[FMT_IDX];
+ dev->in = &dpram->fmt_in;
+ dev->in_buff_addr = dpram->fmt_in_buff;
+ dev->in_buff_size = DP_FMT_IN_BUFF_SIZE;
+ dev->out = &dpram->fmt_out;
+ dev->out_buff_addr = dpram->fmt_out_buff;
+ dev->out_buff_size = DP_FMT_OUT_BUFF_SIZE;
+ 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;
+
+ dev = &dpld->dev_map[RAW_IDX];
+ dev->in = &dpram->raw_in;
+ dev->in_buff_addr = dpram->raw_in_buff;
+ dev->in_buff_size = DP_RAW_IN_BUFF_SIZE;
+ dev->out = &dpram->raw_out;
+ dev->out_buff_addr = dpram->raw_out_buff;
+ dev->out_buff_size = DP_RAW_OUT_BUFF_SIZE;
+ 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;
+}
+
+static int dpram_set_dlmagic(struct link_device *ld, struct io_device *iod)
+{
+ struct dpram_link_device *dpld = to_dpram_link_device(ld);
+ dpram_writew(DP_MAGIC_DMDL, &dpld->dpram->magic);
+ return 0;
+}
+
+static int dpram_set_ulmagic(struct link_device *ld, struct io_device *iod)
+{
+ struct dpram_link_device *dpld = to_dpram_link_device(ld);
+ struct dpram_map *dpram = (void *)dpld->dpram;
+ u8 *dest;
+ dest = (u8 *)(&dpram->fmt_out);
+
+ dpram_writew(DP_MAGIC_UMDL, &dpld->dpram->magic);
+
+ dpram_writeb((u8)START_INDEX, dest + 0);
+ dpram_writeb((u8)0x1, dest + 1);
+ dpram_writeb((u8)0x1, dest + 2);
+ dpram_writeb((u8)0x0, dest + 3);
+ dpram_writeb((u8)END_INDEX, dest + 4);
+
+ init_completion(&dpld->gota_download_start_complete);
+ dpram_write_command(dpld, CMD_DL_START_REQ);
+
+ return 0;
+}
+
+static int
+dpram_download(struct dpram_link_device *dpld, const char *buf, int len)
+{
+ struct dpram_map *dpram = (void *)dpld->dpram;
+ struct dpram_ota_header header;
+ u16 nframes;
+ u16 curframe = 1;
+ u16 plen;
+ u8 *dest;
+ int ret;
+
+ nframes = DIV_ROUND_UP(len, DP_DEFAULT_WRITE_LEN);
+
+ mif_debug("[GOTA] download len = %d\n", len);
+
+ header.start_index = START_INDEX;
+ header.nframes = nframes;
+
+#ifdef CONFIG_INTERNAL_MODEM_IF
+ dpld->board_ota_reset();
+#endif
+ while (len > 0) {
+ plen = min(len, DP_DEFAULT_WRITE_LEN);
+ dest = (u8 *)&dpram->fmt_out;
+
+ mif_debug("[GOTA] Start write frame %d/%d\n", \
+ curframe, nframes);
+
+ header.curframe = curframe;
+ header.len = plen;
+
+ memcpy(dest, &header, sizeof(header));
+ dest += sizeof(header);
+
+ memcpy(dest, buf, plen);
+ dest += plen;
+ buf += plen;
+ len -= plen;
+
+ dpram_writeb(END_INDEX, dest+3);
+
+ init_completion(&dpld->gota_send_done);
+
+ if (curframe == 1) {
+#ifdef CONFIG_INTERNAL_MODEM_IF
+ init_completion(&dpld->gota_download_start_complete);
+ dpram_write_command(dpld, 0);
+#endif
+ ret = wait_for_completion_interruptible_timeout(
+ &dpld->gota_download_start_complete,
+ GOTA_TIMEOUT);
+ if (!ret) {
+ mif_err("[GOTA] CP didn't send DOWNLOAD_START\n");
+ return -ENXIO;
+ }
+ }
+
+ dpram_write_command(dpld, CMD_IMG_SEND_REQ);
+ ret = wait_for_completion_interruptible_timeout(
+ &dpld->gota_send_done, GOTA_SEND_TIMEOUT);
+ if (!ret) {
+ mif_err("[GOTA] CP didn't send SEND_DONE_RESP\n");
+ return -ENXIO;
+ }
+
+ curframe++;
+ }
+
+ dpram_write_command(dpld, CMD_DL_SEND_DONE_REQ);
+ ret = wait_for_completion_interruptible_timeout(
+ &dpld->gota_update_done, GOTA_TIMEOUT);
+ if (!ret) {
+ mif_err("[GOTA] CP didn't send UPDATE_DONE_NOTIFICATION\n");
+ return -ENXIO;
+ }
+
+ return 0;
+}
+
+static int
+dpram_upload(struct dpram_link_device *dpld, struct dpram_firmware *uploaddata)
+{
+ struct dpram_map *dpram = (void *)dpld->dpram;
+ struct ul_header header;
+ u8 *dest;
+ u8 *buff = vmalloc(DP_DEFAULT_DUMP_LEN);
+ u16 plen = 0;
+ u32 tlen = 0;
+ int ret;
+ int region = 0;
+
+ mif_debug("MIF: dpram_upload()\n");
+
+ ret = wait_for_completion_interruptible_timeout(
+ &dpld->gota_download_start_complete,
+ DUMP_START_TIMEOUT);
+ if (!ret) {
+ mif_err("[GOTA] CP didn't send DOWNLOAD_START\n");
+ goto err_out;
+ }
+
+ wake_lock(&dpld->dpram_wake_lock);
+
+ memset(buff, 0, DP_DEFAULT_DUMP_LEN);
+
+ dpram_write_command(dpld, CMD_IMG_SEND_REQ);
+ mif_debug("MIF: write CMD_IMG_SEND_REQ(0x9400)\n");
+
+ while (1) {
+ init_completion(&dpld->dump_receive_done);
+ ret = wait_for_completion_interruptible_timeout(
+ &dpld->dump_receive_done, DUMP_TIMEOUT);
+ if (!ret) {
+ mif_err("MIF: CP didn't send DATA_SEND_DONE_RESP\n");
+ goto err_out;
+ }
+
+ dest = (u8 *)(&dpram->fmt_out);
+
+#ifdef CONFIG_INTERNAL_MODEM_IF
+ header.bop = *(u16 *)(dest);
+ header.total_frame = *(u16 *)(dest + 2);
+ header.curr_frame = *(u16 *)(dest + 4);
+ header.len = *(u16 *)(dest + 6);
+#else
+ header.bop = *(u8 *)(dest);
+ header.total_frame = *(u16 *)(dest + 1);
+ header.curr_frame = *(u16 *)(dest + 3);
+ header.len = *(u16 *)(dest + 5);
+#endif
+
+ mif_err("total frame:%d, current frame:%d, data len:%d\n",
+ header.total_frame, header.curr_frame,
+ header.len);
+
+ dest += DP_DUMP_HEADER_SIZE;
+ plen = min(header.len, (u16)DP_DEFAULT_DUMP_LEN);
+
+ memcpy(buff, dest, plen);
+ dest += plen;
+
+ ret = copy_to_user(uploaddata->firmware + tlen, buff, plen);
+ if (ret < 0) {
+ mif_err("MIF: Copy to user failed\n");
+ goto err_out;
+ }
+
+ tlen += plen;
+
+ if (header.total_frame == header.curr_frame) {
+ if (region) {
+ uploaddata->is_delta = tlen - uploaddata->size;
+ dpram_write_command(dpld, CMD_UL_RECEIVE_RESP);
+ break;
+ } else {
+ uploaddata->size = tlen;
+ region = 1;
+ }
+ }
+ dpram_write_command(dpld, CMD_UL_RECEIVE_RESP);
+ }
+
+ mif_debug("1st dump region data size=%d\n", uploaddata->size);
+ mif_debug("2st dump region data size=%d\n", uploaddata->is_delta);
+
+ init_completion(&dpld->gota_send_done);
+ ret = wait_for_completion_interruptible_timeout(
+ &dpld->gota_send_done, DUMP_TIMEOUT);
+ if (!ret) {
+ mif_err("[GOTA] CP didn't send SEND_DONE_RESP\n");
+ goto err_out;
+ }
+
+ dpram_write_command(dpld, CMD_UL_RECEIVE_DONE_RESP);
+ mif_debug("MIF: write CMD_UL_RECEIVE_DONE_RESP(0x9801)\n");
+
+ dpram_writew(0, &dpld->dpram->magic); /*clear magic code */
+
+ wake_unlock(&dpld->dpram_wake_lock);
+
+ vfree(buff);
+ return 0;
+
+err_out:
+ vfree(buff);
+ dpram_writew(0, &dpld->dpram->magic);
+ mif_err("CDMA dump error out\n");
+ wake_unlock(&dpld->dpram_wake_lock);
+ return -EIO;
+}
+
+struct link_device *dpram_create_link_device(struct platform_device *pdev)
+{
+ int ret;
+ struct dpram_link_device *dpld;
+ struct link_device *ld;
+ struct resource *res;
+ unsigned long flag = 0;
+#ifdef CONFIG_INTERNAL_MODEM_IF
+ struct modem_data *pdata = (struct modem_data *)pdev->dev.platform_data;
+#endif
+ BUILD_BUG_ON(sizeof(struct dpram_map) != DP_DPRAM_SIZE);
+
+ dpld = kzalloc(sizeof(struct dpram_link_device), GFP_KERNEL);
+ if (!dpld)
+ return NULL;
+ ld = &dpld->ld;
+
+ INIT_LIST_HEAD(&dpld->list_of_io_devices);
+ skb_queue_head_init(&ld->sk_fmt_tx_q);
+ skb_queue_head_init(&ld->sk_raw_tx_q);
+ INIT_DELAYED_WORK(&ld->tx_delayed_work, dpram_write_work);
+
+ wake_lock_init(&dpld->dpram_wake_lock, WAKE_LOCK_SUSPEND, "DPRAM");
+
+ init_completion(&dpld->modem_pif_init_done);
+ init_completion(&dpld->dpram_init_cmd);
+ init_completion(&dpld->gota_send_done);
+ init_completion(&dpld->gota_update_done);
+ init_completion(&dpld->gota_download_start_complete);
+ init_completion(&dpld->dump_receive_done);
+
+ ld->name = "dpram";
+ ld->attach = dpram_attach_io_dev;
+ ld->send = dpram_send;
+ ld->gota_start = dpram_set_dlmagic;
+ ld->modem_update = dpram_modem_update;
+ ld->dump_start = dpram_set_ulmagic;
+ ld->dump_update = dpram_dump_update;
+
+#ifdef CONFIG_INTERNAL_MODEM_IF
+ dpld->clear_interrupt = pdata->clear_intr;
+ dpld->board_ota_reset = pdata->ota_reset;
+#endif
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ mif_err("MIF: Failed to get mem region\n");
+ goto err;
+ }
+ dpld->dpram = ioremap(res->start, resource_size(res));
+ printk(KERN_INFO " MIF: start address:0x%08x\n", \
+ res->start);
+ dpld->irq = platform_get_irq_byname(pdev, "dpram_irq");
+ if (!dpld->irq) {
+ mif_err("MIF: %s: Failed to get IRQ\n", __func__);
+ goto err;
+ }
+
+ dpram_table_init(dpld);
+
+ atomic_set(&dpld->raw_txq_req_ack_rcvd, 0);
+ atomic_set(&dpld->fmt_txq_req_ack_rcvd, 0);
+
+ dpram_writeh(0, &dpld->dpram->magic);
+#ifdef CONFIG_INTERNAL_MODEM_IF
+ ret = idpram_link_pm_init(dpld, pdev);
+
+ if (ret)
+ mif_err("MIF: idpram_link_pm_init fail.(%d)\n", ret);
+#endif
+ flag = IRQF_DISABLED;
+ printk(KERN_ERR "dpram irq: %d\n", dpld->irq);
+ dpld->clear_interrupt();
+ ret = request_irq(dpld->irq, dpram_irq_handler, flag,
+ "dpram irq", ld);
+ if (ret) {
+ mif_err("MIF: DPRAM interrupt handler failed\n");
+ goto err;
+ }
+
+ return ld;
+
+err:
+ iounmap(dpld->dpram);
+ kfree(dpld);
+ return NULL;
+}
diff --git a/drivers/misc/modem_if_na/modem_link_device_dpram.h b/drivers/misc/modem_if_na/modem_link_device_dpram.h
new file mode 100644
index 0000000..aaa8e82
--- /dev/null
+++ b/drivers/misc/modem_if_na/modem_link_device_dpram.h
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2011 Google, Inc.
+ * Copyright (C) 2010 Samsung Electronics.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/wakelock.h>
+
+#ifndef __MODEM_LINK_DEVICE_DPRAM_H__
+#define __MODEM_LINK_DEVICE_DPRAM_H__
+
+#define FMT_IDX 0
+#define RAW_IDX 1
+#define MAX_IDX 2
+#define DP_FMT_OUT_BUFF_SIZE 2044
+#define DP_RAW_OUT_BUFF_SIZE 6128
+#define DP_FMT_IN_BUFF_SIZE 2044
+#define DP_RAW_IN_BUFF_SIZE 6128
+struct dpram_circ {
+ u16 head;
+ u16 tail;
+};
+
+struct dpram_ota_header {
+ u8 start_index;
+ u16 nframes;
+ u16 curframe;
+ u16 len;
+
+} __packed;
+
+struct dpram_map {
+ u16 magic;
+ u16 enable;
+
+ struct dpram_circ fmt_out;
+ u8 fmt_out_buff[DP_FMT_OUT_BUFF_SIZE];
+
+ struct dpram_circ raw_out;
+ u8 raw_out_buff[DP_RAW_OUT_BUFF_SIZE];
+
+ struct dpram_circ fmt_in;
+ u8 fmt_in_buff[DP_FMT_IN_BUFF_SIZE];
+
+ struct dpram_circ raw_in;
+ u8 raw_in_buff[DP_RAW_IN_BUFF_SIZE];
+
+ u8 padding[16];
+#ifdef CONFIG_INTERNAL_MODEM_IF
+ u16 mbx_ap2cp;
+ u16 mbx_cp2ap;
+#else
+ u16 mbx_cp2ap;
+ u16 mbx_ap2cp;
+#endif
+} __packed;
+
+struct dpram_device {
+ struct dpram_circ __iomem *in;
+ u8 __iomem *in_buff_addr;
+ int in_buff_size;
+
+ struct dpram_circ __iomem *out;
+ u8 __iomem *out_buff_addr;
+ int out_buff_size;
+
+ u16 mask_req_ack;
+ u16 mask_res_ack;
+ u16 mask_send;
+};
+
+struct ul_header {
+#ifdef CONFIG_INTERNAL_MODEM_IF
+ u16 bop;
+ u16 total_frame;
+ u16 curr_frame;
+ u16 len;
+#else
+ u8 bop;
+ u16 total_frame;
+ u16 curr_frame;
+ u16 len;
+#endif
+};
+
+#ifdef CONFIG_INTERNAL_MODEM_IF
+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_link_pm_data {
+ struct dpram_link_device *dpld;
+ struct modem_data *mdata;
+
+ unsigned last_pm_mailbox; /* 0xCC or 0x CA*/
+ unsigned resume_retry;
+ unsigned pm_states;
+
+ int idpram_wpend;
+
+ struct completion idpram_down;
+
+ struct delayed_work resume_work;
+
+ atomic_t read_lock;
+ atomic_t write_lock;
+
+ struct wake_lock host_wakeup_wlock;
+ struct wake_lock rd_wlock;
+ struct wake_lock hold_wlock;
+ struct wake_lock wakeup_wlock;
+};
+#endif
+
+
+struct dpram_link_device {
+ struct link_device ld;
+
+ /* maybe -list of io devices for the link device to use
+ * to find where to send incoming packets to */
+ struct list_head list_of_io_devices;
+
+ atomic_t raw_txq_req_ack_rcvd;
+ atomic_t fmt_txq_req_ack_rcvd;
+
+ struct dpram_map __iomem *dpram;
+ struct dpram_device dev_map[MAX_IDX];
+
+ struct wake_lock dpram_wake_lock;
+
+ struct completion dpram_init_cmd;
+ struct completion modem_pif_init_done;
+ struct completion gota_download_start_complete;
+ struct completion gota_send_done;
+ struct completion gota_update_done;
+ struct completion dump_receive_done;
+
+ int irq;
+#ifdef CONFIG_INTERNAL_MODEM_IF
+ void (*clear_interrupt)(void);
+ int (*board_ota_reset) (void);
+ struct idpram_link_pm_data *link_pm_data;
+ int (*init_magic_num)(struct dpram_link_device *dpld);
+#else
+ void (*clear_interrupt)(struct dpram_link_device *);
+#endif
+};
+
+/* converts from struct link_device* to struct xxx_link_device* */
+#define to_dpram_link_device(linkdev) \
+ container_of(linkdev, struct dpram_link_device, ld)
+
+#endif
diff --git a/drivers/misc/modem_if_na/modem_link_device_usb.c b/drivers/misc/modem_if_na/modem_link_device_usb.c
new file mode 100644
index 0000000..6bdd7b5
--- /dev/null
+++ b/drivers/misc/modem_if_na/modem_link_device_usb.c
@@ -0,0 +1,1031 @@
+/*
+ * Copyright (C) 2010 Google, Inc.
+ * Copyright (C) 2010 Samsung Electronics.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#define DEBUG
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/sched.h>
+#include <linux/irq.h>
+#include <linux/poll.h>
+#include <linux/gpio.h>
+#include <linux/if_arp.h>
+#include <linux/platform_device.h>
+#include <linux/suspend.h>
+
+#include <linux/platform_data/modem_na.h>
+#include "modem_prj.h"
+#include "modem_link_device_usb.h"
+#include "modem_link_pm_usb.h"
+
+#include <mach/regs-gpio.h>
+
+#define URB_COUNT 4
+
+extern int factory_mode;
+
+static int enable_autosuspend;
+static int wakelock_held;
+
+static int usb_tx_urb_with_skb(struct usb_link_device *usb_ld,
+ struct sk_buff *skb, struct if_usb_devdata *pipe_data);
+
+static void
+usb_change_modem_state(struct usb_link_device *usb_ld, enum modem_state state);
+
+static int usb_attach_io_dev(struct link_device *ld,
+ struct io_device *iod)
+{
+ struct usb_link_device *usb_ld = to_usb_link_device(ld);
+
+ iod->link = ld;
+
+ /* list up io devices */
+ list_add(&iod->list, &usb_ld->list_of_io_devices);
+
+ return 0;
+}
+
+static void
+usb_free_urbs(struct usb_link_device *usb_ld, struct if_usb_devdata *pipe)
+{
+ struct usb_device *usbdev = usb_ld->usbdev;
+ struct urb *urb;
+
+ while ((urb = usb_get_from_anchor(&pipe->urbs))) {
+ usb_poison_urb(urb);
+ usb_free_coherent(usbdev, pipe->rx_buf_size,
+ urb->transfer_buffer, urb->transfer_dma);
+ urb->transfer_buffer = NULL;
+ usb_put_urb(urb);
+ usb_free_urb(urb);
+ }
+}
+
+static int usb_init_communication(struct link_device *ld,
+ struct io_device *iod)
+{
+ int err = 0;
+ switch (iod->format) {
+ case IPC_BOOT:
+ ld->com_state = COM_BOOT;
+ skb_queue_purge(&ld->sk_fmt_tx_q);
+ break;
+
+ case IPC_RAMDUMP:
+ ld->com_state = COM_CRASH;
+ break;
+
+ case IPC_FMT:
+ case IPC_RFS:
+ case IPC_RAW:
+
+ default:
+ ld->com_state = COM_ONLINE;
+ break;
+ }
+
+ mif_debug("com_state = %d\n", ld->com_state);
+ return err;
+}
+
+static void usb_terminate_communication(
+ struct link_device *ld, struct io_device *iod)
+{
+ if (iod->format != IPC_BOOT && iod->format != IPC_RAMDUMP)
+ mif_debug("com_state = %d\n", ld->com_state);
+}
+
+static int usb_rx_submit(struct if_usb_devdata *pipe, struct urb *urb,
+ gfp_t gfp_flags)
+{
+ int ret;
+
+ usb_anchor_urb(urb, &pipe->reading);
+ ret = usb_submit_urb(urb, gfp_flags);
+ if (ret) {
+ usb_unanchor_urb(urb);
+ usb_anchor_urb(urb, &pipe->urbs);
+ mif_err("submit urb fail with ret (%d)\n", ret);
+ }
+
+ usb_mark_last_busy(urb->dev);
+ return ret;
+}
+
+static void usb_rx_complete(struct urb *urb)
+{
+ struct if_usb_devdata *pipe_data = urb->context;
+ struct usb_link_device *usb_ld = usb_get_intfdata(pipe_data->data_intf);
+ struct io_device *iod;
+ int iod_format = IPC_FMT;
+ int ret;
+
+ usb_mark_last_busy(urb->dev);
+
+ switch (urb->status) {
+ case 0:
+ case -ENOENT:
+ if (!urb->actual_length)
+ goto re_submit;
+ /* call iod recv */
+ /* how we can distinguish boot ch with fmt ch ?? */
+ switch (pipe_data->format) {
+ case IF_USB_FMT_EP:
+ iod_format = IPC_FMT;
+ /*
+ print_hex_dump(KERN_INFO, "[FMT-RX] ",
+ DUMP_PREFIX_OFFSET, 16, 1,
+ urb->transfer_buffer,
+ min(urb->actual_length, (u32)64), true);
+ */
+ break;
+ case IF_USB_RAW_EP:
+ iod_format = IPC_MULTI_RAW;
+ break;
+ case IF_USB_RFS_EP:
+ iod_format = IPC_RFS;
+ break;
+ default:
+ break;
+ }
+
+ list_for_each_entry(iod, &usb_ld->list_of_io_devices, list) {
+ /* during boot stage fmt end point */
+ /* shared with boot io device */
+ /* when we use fmt device only, at boot and ipc exchange
+ it can be reduced to 1 device */
+ if (iod_format == IPC_FMT &&
+ usb_ld->ld.com_state == COM_BOOT)
+ iod_format = IPC_BOOT;
+ if (iod_format == IPC_FMT &&
+ usb_ld->ld.com_state == COM_CRASH)
+ iod_format = IPC_RAMDUMP;
+
+ if (iod->format == iod_format) {
+ ret = iod->recv(iod,
+ (char *)urb->transfer_buffer,
+ urb->actual_length);
+ if (ret < 0)
+ mif_err("io device recv error :%d\n",
+ ret);
+ break;
+ }
+ }
+re_submit:
+ if (urb->status || atomic_read(&usb_ld->suspend_count))
+ break;
+
+ usb_mark_last_busy(urb->dev);
+ usb_rx_submit(pipe_data, urb, GFP_ATOMIC);
+ return;
+ case -ESHUTDOWN:
+ case -EPROTO:
+ break;
+ case -EOVERFLOW:
+ mif_err("RX overflow\n");
+ break;
+ default:
+ mif_err("RX complete Status (%d)\n", urb->status);
+ break;
+ }
+
+ usb_anchor_urb(urb, &pipe_data->urbs);
+}
+
+static int usb_send(struct link_device *ld, struct io_device *iod,
+ struct sk_buff *skb)
+{
+ struct sk_buff_head *txq;
+
+ if (iod->format == IPC_RAW)
+ txq = &ld->sk_raw_tx_q;
+ else
+ txq = &ld->sk_fmt_tx_q;
+
+ /* save io device into cb area */
+ *((struct io_device **)skb->cb) = iod;
+ /* en queue skb data */
+ skb_queue_tail(txq, skb);
+
+ queue_delayed_work(ld->tx_wq, &ld->tx_delayed_work, 0);
+
+ return skb->len;
+}
+
+static void usb_tx_complete(struct urb *urb)
+{
+ int ret = 0;
+ struct sk_buff *skb = urb->context;
+
+ switch (urb->status) {
+ case 0:
+ break;
+ default:
+ mif_err("TX error (%d)\n", urb->status);
+ }
+
+ usb_mark_last_busy(urb->dev);
+ ret = pm_runtime_put_autosuspend(&urb->dev->dev);
+ if (ret < 0)
+ mif_debug("pm_runtime_put_autosuspend failed : ret(%d)\n", ret);
+ usb_free_urb(urb);
+ dev_kfree_skb_any(skb);
+}
+
+static void if_usb_force_disconnect(struct work_struct *work)
+{
+ struct usb_link_device *usb_ld =
+ container_of(work, struct usb_link_device, disconnect_work);
+ struct usb_device *udev = usb_ld->usbdev;
+
+ if (!udev || !(&udev->dev))
+ return;
+
+ pm_runtime_get_sync(&udev->dev);
+ if (udev->state != USB_STATE_NOTATTACHED) {
+ usb_force_disconnect(udev);
+ mif_info("force disconnect by modem not responding!!\n");
+ }
+ pm_runtime_put_autosuspend(&udev->dev);
+}
+
+static void
+usb_change_modem_state(struct usb_link_device *usb_ld, enum modem_state state)
+{
+ struct io_device *iod;
+
+ list_for_each_entry(iod, &usb_ld->list_of_io_devices, list) {
+ if (iod->format == IPC_FMT) {
+ iod->modem_state_changed(iod, state);
+ return;
+ }
+ }
+}
+
+static int usb_tx_urb_with_skb(struct usb_link_device *usb_ld,
+ struct sk_buff *skb, struct if_usb_devdata *pipe_data)
+{
+ int ret, cnt = 0;
+ struct urb *urb;
+ struct usb_device *usbdev = usb_ld->usbdev;
+ unsigned long flags;
+
+ if (!usbdev || (usbdev->state == USB_STATE_NOTATTACHED) ||
+ usb_ld->host_wake_timeout_flag)
+ return -ENODEV;
+
+ pm_runtime_get_noresume(&usbdev->dev);
+
+ if (usbdev->dev.power.runtime_status == RPM_SUSPENDED ||
+ usbdev->dev.power.runtime_status == RPM_SUSPENDING) {
+ usb_ld->resume_status = AP_INITIATED_RESUME;
+ SET_SLAVE_WAKEUP(usb_ld->pdata, 1);
+
+ while (!wait_event_interruptible_timeout(usb_ld->l2_wait,
+ usbdev->dev.power.runtime_status == RPM_ACTIVE
+ || pipe_data->disconnected,
+ HOST_WAKEUP_TIMEOUT_JIFFIES)) {
+
+ if (cnt == MAX_RETRY) {
+ mif_err("host wakeup timeout !!\n");
+ SET_SLAVE_WAKEUP(usb_ld->pdata, 0);
+ pm_runtime_put_autosuspend(&usbdev->dev);
+ schedule_work(&usb_ld->disconnect_work);
+ usb_ld->host_wake_timeout_flag = 1;
+ return -1;
+ }
+ mif_err("host wakeup timeout ! retry..\n");
+ SET_SLAVE_WAKEUP(usb_ld->pdata, 0);
+ udelay(100);
+ SET_SLAVE_WAKEUP(usb_ld->pdata, 1);
+ cnt++;
+ }
+
+ if (pipe_data->disconnected) {
+ SET_SLAVE_WAKEUP(usb_ld->pdata, 0);
+ pm_runtime_put_autosuspend(&usbdev->dev);
+ return -ENODEV;
+ }
+
+ mif_debug("wait_q done (runtime_status=%d)\n",
+ usbdev->dev.power.runtime_status);
+ }
+
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb) {
+ mif_err("alloc urb error\n");
+ if (pm_runtime_put_autosuspend(&usbdev->dev) < 0)
+ mif_debug("pm_runtime_put_autosuspend fail\n");
+ return -ENOMEM;
+ }
+
+ urb->transfer_flags = URB_ZERO_PACKET;
+ usb_fill_bulk_urb(urb, usbdev, pipe_data->tx_pipe, skb->data,
+ skb->len, usb_tx_complete, (void *)skb);
+
+ spin_lock_irqsave(&usb_ld->lock, flags);
+ if (atomic_read(&usb_ld->suspend_count)) {
+ /* transmission will be done in resume */
+ usb_anchor_urb(urb, &usb_ld->deferred);
+ usb_put_urb(urb);
+ mif_debug("anchor urb (0x%p)\n", urb);
+ spin_unlock_irqrestore(&usb_ld->lock, flags);
+ return 0;
+ }
+ spin_unlock_irqrestore(&usb_ld->lock, flags);
+
+ ret = usb_submit_urb(urb, GFP_KERNEL);
+ if (ret < 0) {
+ mif_err("usb_submit_urb with ret(%d)\n", ret);
+ if (pm_runtime_put_autosuspend(&usbdev->dev) < 0)
+ mif_debug("pm_runtime_put_autosuspend fail\n");
+ }
+ return ret;
+}
+
+static void usb_tx_work(struct work_struct *work)
+{
+ int ret = 0;
+ struct link_device *ld =
+ container_of(work, struct link_device, tx_delayed_work.work);
+ struct usb_link_device *usb_ld = to_usb_link_device(ld);
+ struct io_device *iod;
+ struct sk_buff *skb;
+ struct if_usb_devdata *pipe_data;
+ struct link_pm_data *pm_data = usb_ld->link_pm_data;
+
+ /*TODO: check the PHONE ACTIVE STATES */
+ /* because tx data wait until hub on with wait_for_complettion, it
+ should queue to single_threaded work queue */
+ if (!link_pm_set_active(usb_ld))
+ return;
+
+ while (ld->sk_fmt_tx_q.qlen || ld->sk_raw_tx_q.qlen) {
+ /* send skb from fmt_txq and raw_txq,
+ * one by one for fair flow control */
+ skb = skb_dequeue(&ld->sk_fmt_tx_q);
+ if (skb) {
+ iod = *((struct io_device **)skb->cb);
+ switch (iod->format) {
+ case IPC_FMT:
+ /*
+ print_hex_dump(KERN_INFO, "[FMT-TX] ",
+ DUMP_PREFIX_OFFSET, 16, 1,
+ skb->data,
+ min(skb->len, (u32)64), true);
+ */
+ case IPC_BOOT:
+ case IPC_RAMDUMP:
+ /* boot device uses same intf with fmt*/
+ pipe_data = &usb_ld->devdata[IF_USB_FMT_EP];
+ break;
+ case IPC_RFS:
+ pipe_data = &usb_ld->devdata[IF_USB_RFS_EP];
+ break;
+ default:
+ /* wrong packet for fmt tx q , drop it */
+ dev_kfree_skb_any(skb);
+ continue;
+ }
+
+ ret = usb_tx_urb_with_skb(usb_ld, skb, pipe_data);
+ if (ret < 0) {
+ mif_err("usb_tx_urb_with_skb, ret(%d)\n",
+ ret);
+ skb_queue_head(&ld->sk_fmt_tx_q, skb);
+
+ if (edc_inc(&usb_ld->urb_edc, EDC_MAX_ERRORS,
+ EDC_ERROR_TIMEFRAME)) {
+ mif_err("maximum error in URB exceeded\n");
+ usb_change_modem_state(usb_ld,
+ STATE_CRASH_EXIT);
+ }
+ return;
+ }
+ }
+
+ skb = skb_dequeue(&ld->sk_raw_tx_q);
+ if (skb) {
+ pipe_data = &usb_ld->devdata[IF_USB_RAW_EP];
+ ret = usb_tx_urb_with_skb(usb_ld, skb, pipe_data);
+ if (ret < 0) {
+ mif_err("usb_tx_urb_with_skb "
+ "for raw, ret(%d)\n",
+ ret);
+ skb_queue_head(&ld->sk_raw_tx_q, skb);
+
+ if (edc_inc(&usb_ld->urb_edc, EDC_MAX_ERRORS,
+ EDC_ERROR_TIMEFRAME)) {
+ mif_err("maximum error in URB exceeded\n");
+ usb_change_modem_state(usb_ld,
+ STATE_CRASH_EXIT);
+ }
+ return;
+ }
+ }
+ }
+}
+
+static int if_usb_suspend(struct usb_interface *intf, pm_message_t message)
+{
+ struct usb_link_device *usb_ld = usb_get_intfdata(intf);
+ int i;
+
+ if (atomic_inc_return(&usb_ld->suspend_count) == IF_USB_DEVNUM_MAX) {
+ mif_debug("L2\n");
+
+ for (i = 0; i < IF_USB_DEVNUM_MAX; i++)
+ usb_kill_anchored_urbs(&usb_ld->devdata[i].reading);
+
+ /*
+ if (usb_ld->link_pm_data->cpufreq_unlock)
+ usb_ld->link_pm_data->cpufreq_unlock();
+ */
+ if (wakelock_held) {
+ wakelock_held = 0;
+ wake_unlock(&usb_ld->susplock);
+ }
+ }
+
+ return 0;
+}
+
+static void runtime_pm_work(struct work_struct *work)
+{
+ struct usb_link_device *usb_ld = container_of(work,
+ struct usb_link_device, runtime_pm_work.work);
+ int ret;
+
+ ret = pm_request_autosuspend(&usb_ld->usbdev->dev);
+
+ if (ret == -EAGAIN || ret == 1)
+ queue_delayed_work(system_nrt_wq, &usb_ld->runtime_pm_work,
+ msecs_to_jiffies(50));
+}
+
+static void post_resume_work(struct work_struct *work)
+{
+ struct usb_link_device *usb_ld = container_of(work,
+ struct usb_link_device, post_resume_work.work);
+
+ /* lock cpu frequency when L2->L0 */
+ /*
+ if (usb_ld->link_pm_data->cpufreq_lock)
+ usb_ld->link_pm_data->cpufreq_lock();
+ */
+}
+
+static void wait_enumeration_work(struct work_struct *work)
+{
+ struct usb_link_device *usb_ld = container_of(work,
+ struct usb_link_device, wait_enumeration.work);
+
+ /* Work around code for factory device test & verification.
+ * To measure device sleep current speedly, do not call silent reset in
+ * factory mode. CMC22x disconect usb forcely and go sleep
+ * without normal L3 process if in factory mode. Also AP do. */
+ if (factory_mode)
+ return;
+
+ if (usb_ld->if_usb_connected == 0) {
+ mif_err("USB disconnected and not enumerated for long time\n");
+ usb_change_modem_state(usb_ld, STATE_CRASH_EXIT);
+ }
+}
+
+static int if_usb_resume(struct usb_interface *intf)
+{
+ int i, ret;
+ struct sk_buff *skb;
+ struct usb_link_device *usb_ld = usb_get_intfdata(intf);
+ struct if_usb_devdata *pipe;
+ struct urb *urb;
+
+ spin_lock_irq(&usb_ld->lock);
+ if (!atomic_dec_return(&usb_ld->suspend_count)) {
+ spin_unlock_irq(&usb_ld->lock);
+
+ mif_debug("\n");
+ wake_lock(&usb_ld->susplock);
+ wakelock_held = 1;
+
+ /* HACK: Runtime pm does not allow requesting autosuspend from
+ * resume callback, delayed it after resume */
+ queue_delayed_work(system_nrt_wq, &usb_ld->runtime_pm_work,
+ msecs_to_jiffies(50));
+
+ for (i = 0; i < IF_USB_DEVNUM_MAX; i++) {
+ pipe = &usb_ld->devdata[i];
+ while ((urb = usb_get_from_anchor(&pipe->urbs))) {
+ ret = usb_rx_submit(pipe, urb, GFP_KERNEL);
+ if (ret < 0) {
+ usb_put_urb(urb);
+ mif_err(
+ "usb_rx_submit error with (%d)\n",
+ ret);
+ return ret;
+ }
+ usb_put_urb(urb);
+ }
+ }
+
+ while ((urb = usb_get_from_anchor(&usb_ld->deferred))) {
+ mif_debug("got urb (0x%p) from anchor & resubmit\n",
+ urb);
+ ret = usb_submit_urb(urb, GFP_KERNEL);
+ if (ret < 0) {
+ mif_err("resubmit failed\n");
+ skb = urb->context;
+ dev_kfree_skb_any(skb);
+ usb_free_urb(urb);
+ if (pm_runtime_put_autosuspend(
+ &usb_ld->usbdev->dev) < 0)
+ mif_debug(
+ "pm_runtime_put_autosuspend fail\n");
+ }
+ }
+ SET_SLAVE_WAKEUP(usb_ld->pdata, 1);
+ udelay(100);
+ SET_SLAVE_WAKEUP(usb_ld->pdata, 0);
+
+ if (!link_pm_is_connected(usb_ld))
+ enable_autosuspend = 1;
+
+ /* if_usb_resume() is atomic. post_resume_work is
+ * a kind of bottom halves
+ */
+ /*
+ queue_delayed_work(system_nrt_wq, &usb_ld->post_resume_work, 0);
+ */
+
+ return 0;
+ }
+
+ spin_unlock_irq(&usb_ld->lock);
+ return 0;
+}
+
+static int if_usb_reset_resume(struct usb_interface *intf)
+{
+ int ret;
+
+ mif_debug("\n");
+ ret = if_usb_resume(intf);
+ return ret;
+}
+
+static struct usb_device_id if_usb_ids[] = {
+ { USB_DEVICE(0x04e8, 0x6999), /* CMC221 LTE Modem */
+ /*.driver_info = 0,*/
+ },
+ { } /* terminating entry */
+};
+MODULE_DEVICE_TABLE(usb, if_usb_ids);
+
+static struct usb_driver if_usb_driver;
+static void if_usb_disconnect(struct usb_interface *intf)
+{
+ struct usb_link_device *usb_ld = usb_get_intfdata(intf);
+ struct usb_device *usbdev = usb_ld->usbdev;
+ int dev_id = intf->altsetting->desc.bInterfaceNumber;
+ struct if_usb_devdata *pipe_data = &usb_ld->devdata[dev_id];
+
+ usb_set_intfdata(intf, NULL);
+
+ pipe_data->disconnected = 1;
+ smp_wmb();
+
+ wake_up(&usb_ld->l2_wait);
+ if (wakelock_held) {
+ wakelock_held = 0;
+ wake_unlock(&usb_ld->susplock);
+ }
+ /*
+ if (usb_ld->if_usb_connected) {
+ disable_irq_wake(usb_ld->pdata->irq_host_wakeup);
+ free_irq(usb_ld->pdata->irq_host_wakeup, usb_ld);
+ }
+ */
+
+ usb_ld->if_usb_connected = 0;
+ usb_ld->flow_suspend = 1;
+
+ dev_dbg(&usbdev->dev, "%s\n", __func__);
+ usb_ld->dev_count--;
+ usb_driver_release_interface(&if_usb_driver, pipe_data->data_intf);
+
+ usb_kill_anchored_urbs(&pipe_data->reading);
+ usb_free_urbs(usb_ld, pipe_data);
+
+ if (usb_ld->dev_count == 0) {
+ cancel_delayed_work_sync(&usb_ld->runtime_pm_work);
+ /*
+ cancel_delayed_work_sync(&usb_ld->post_resume_work);
+ */
+ cancel_delayed_work_sync(&usb_ld->ld.tx_delayed_work);
+ cancel_work_sync(&usb_ld->disconnect_work);
+ usb_put_dev(usbdev);
+ usb_ld->usbdev = NULL;
+
+ wake_lock(&usb_ld->link_pm_data->boot_wake);
+
+ schedule_delayed_work(&usb_ld->wait_enumeration,
+ WAIT_ENUMURATION_TIMEOUT_JIFFIES);
+ }
+}
+
+static int __devinit if_usb_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct usb_host_interface *data_desc;
+ struct usb_link_device *usb_ld =
+ (struct usb_link_device *)id->driver_info;
+ struct link_device *ld = &usb_ld->ld;
+ struct usb_interface *data_intf;
+ struct usb_device *usbdev = interface_to_usbdev(intf);
+ struct device *dev;
+ struct if_usb_devdata *pipe;
+ struct urb *urb;
+ int i;
+ int j;
+ int dev_id;
+ int err;
+
+ /* To detect usb device order probed */
+ dev_id = intf->cur_altsetting->desc.bInterfaceNumber;
+
+ if (dev_id >= IF_USB_DEVNUM_MAX) {
+ dev_err(&intf->dev, "Device id %d cannot support\n",
+ dev_id);
+ return -EINVAL;
+ }
+
+ if (!usb_ld) {
+ dev_err(&intf->dev,
+ "if_usb device doesn't be allocated\n");
+ err = ENOMEM;
+ goto out;
+ }
+
+ mif_info("probe dev_id=%d usb_device_id(0x%p), usb_ld (0x%p)\n",
+ dev_id, id, usb_ld);
+
+ usb_ld->usbdev = usbdev;
+
+ usb_get_dev(usbdev);
+
+ for (i = 0; i < IF_USB_DEVNUM_MAX; i++) {
+ data_intf = usb_ifnum_to_if(usbdev, i);
+
+ /* remap endpoint of RAW to no.1 for LTE modem */
+ if (i == 0)
+ pipe = &usb_ld->devdata[1];
+ else if (i == 1)
+ pipe = &usb_ld->devdata[0];
+ else
+ pipe = &usb_ld->devdata[i];
+
+ pipe->disconnected = 0;
+ pipe->data_intf = data_intf;
+ data_desc = data_intf->cur_altsetting;
+
+ /* Endpoints */
+ if (usb_pipein(data_desc->endpoint[0].desc.bEndpointAddress)) {
+ pipe->rx_pipe = usb_rcvbulkpipe(usbdev,
+ data_desc->endpoint[0].desc.bEndpointAddress);
+ pipe->tx_pipe = usb_sndbulkpipe(usbdev,
+ data_desc->endpoint[1].desc.bEndpointAddress);
+ pipe->rx_buf_size = 1024*4;
+ } else {
+ pipe->rx_pipe = usb_rcvbulkpipe(usbdev,
+ data_desc->endpoint[1].desc.bEndpointAddress);
+ pipe->tx_pipe = usb_sndbulkpipe(usbdev,
+ data_desc->endpoint[0].desc.bEndpointAddress);
+ pipe->rx_buf_size = 1024*4;
+ }
+
+ if (i == 0) {
+ dev_info(&usbdev->dev, "USB IF USB device found\n");
+ } else {
+ err = usb_driver_claim_interface(&if_usb_driver,
+ data_intf, usb_ld);
+ if (err < 0) {
+ mif_err("failed to cliam usb interface\n");
+ goto out;
+ }
+ }
+
+ usb_set_intfdata(data_intf, usb_ld);
+ usb_ld->dev_count++;
+ pm_suspend_ignore_children(&data_intf->dev, true);
+
+ for (j = 0; j < URB_COUNT; j++) {
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb) {
+ mif_err("alloc urb fail\n");
+ err = -ENOMEM;
+ goto out2;
+ }
+
+ urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+ urb->transfer_buffer = usb_alloc_coherent(usbdev,
+ pipe->rx_buf_size, GFP_KERNEL,
+ &urb->transfer_dma);
+ if (!urb->transfer_buffer) {
+ mif_err(
+ "Failed to allocate transfer buffer\n");
+ usb_free_urb(urb);
+ err = -ENOMEM;
+ goto out2;
+ }
+
+ usb_fill_bulk_urb(urb, usbdev, pipe->rx_pipe,
+ urb->transfer_buffer, pipe->rx_buf_size,
+ usb_rx_complete, pipe);
+ usb_anchor_urb(urb, &pipe->urbs);
+ }
+ }
+
+ /* temporary call reset_resume */
+ atomic_set(&usb_ld->suspend_count, 1);
+ if_usb_reset_resume(data_intf);
+ atomic_set(&usb_ld->suspend_count, 0);
+
+ SET_HOST_ACTIVE(usb_ld->pdata, 1);
+ usb_ld->host_wake_timeout_flag = 0;
+
+ if (gpio_get_value(usb_ld->pdata->gpio_phone_active)) {
+ struct link_pm_data *pm_data = usb_ld->link_pm_data;
+ int delay = AUTOSUSPEND_DELAY_MS;
+ pm_runtime_set_autosuspend_delay(&usbdev->dev, delay);
+ dev = &usb_ld->usbdev->dev;
+ if (dev->parent) {
+ dev_dbg(&usbdev->dev, "if_usb Runtime PM Start!!\n");
+ usb_enable_autosuspend(usb_ld->usbdev);
+ pm_runtime_allow(dev);
+
+ if (pm_data->block_autosuspend)
+ pm_runtime_forbid(dev);
+ }
+
+ enable_irq_wake(usb_ld->pdata->irq_host_wakeup);
+
+ usb_ld->flow_suspend = 0;
+ /* Queue work if skbs were pending before a disconnect/probe */
+ if (ld->sk_fmt_tx_q.qlen || ld->sk_raw_tx_q.qlen)
+ queue_delayed_work(ld->tx_wq, &ld->tx_delayed_work, 0);
+
+ wake_unlock(&usb_ld->link_pm_data->boot_wake);
+
+ usb_ld->if_usb_connected = 1;
+ usb_change_modem_state(usb_ld, STATE_ONLINE);
+ } else {
+ usb_change_modem_state(usb_ld, STATE_LOADER_DONE);
+ }
+
+ edc_init(&usb_ld->urb_edc);
+
+ return 0;
+
+out2:
+ usb_ld->dev_count--;
+ for (i = 0; i < IF_USB_DEVNUM_MAX; i++)
+ usb_free_urbs(usb_ld, &usb_ld->devdata[i]);
+out:
+ usb_set_intfdata(intf, NULL);
+ return err;
+}
+
+irqreturn_t usb_resume_irq(int irq, void *data)
+{
+ int ret;
+ struct usb_link_device *usb_ld = data;
+ int hwup;
+ static int wake_status = -1;
+ struct device *dev;
+
+ hwup = gpio_get_value(usb_ld->pdata->gpio_host_wakeup);
+ if (hwup == wake_status) {
+ mif_err("Received spurious wake irq: %d", hwup);
+ return IRQ_HANDLED;
+ }
+ wake_status = hwup;
+
+ irq_set_irq_type(irq, hwup ? IRQF_TRIGGER_LOW : IRQF_TRIGGER_HIGH);
+ /*
+ * exynos BSP has problem when using level interrupt.
+ * If we change irq type from interrupt handler,
+ * we can get level interrupt twice.
+ * this is temporary solution until SYS.LSI resolve this problem.
+ */
+ __raw_writel(eint_irq_to_bit(irq), S5P_EINT_PEND(EINT_REG_NR(irq)));
+ wake_lock_timeout(&usb_ld->gpiolock, 100);
+
+ mif_err("< H-WUP %d\n", hwup);
+
+ if (!link_pm_is_connected(usb_ld) &&
+ !(!hwup && enable_autosuspend)) {
+ return IRQ_HANDLED;
+ } else {
+ enable_autosuspend = 0;
+ }
+
+ if (hwup) {
+ dev = &usb_ld->usbdev->dev;
+ mif_info("runtime status=%d\n",
+ dev->power.runtime_status);
+
+ /* if usb3503 was on, usb_if was resumed by probe */
+ /*
+ if (has_hub(usb_ld) &&
+ (dev->power.runtime_status == RPM_ACTIVE ||
+ dev->power.runtime_status == RPM_RESUMING))
+ return IRQ_HANDLED;
+ */
+
+ device_lock(dev);
+ if (dev->power.is_prepared || dev->power.is_suspended) {
+ pm_runtime_get_noresume(dev);
+ ret = 0;
+ } else {
+ ret = pm_runtime_get_sync(dev);
+ }
+ device_unlock(dev);
+ if (ret < 0) {
+ mif_err("pm_runtime_get fail (%d)\n", ret);
+ return IRQ_HANDLED;
+ }
+ } else {
+ if (usb_ld->resume_status == AP_INITIATED_RESUME)
+ wake_up(&usb_ld->l2_wait);
+ usb_ld->resume_status = CP_INITIATED_RESUME;
+ pm_runtime_mark_last_busy(&usb_ld->usbdev->dev);
+ pm_runtime_put_autosuspend(&usb_ld->usbdev->dev);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int if_usb_init(struct usb_link_device *usb_ld)
+{
+ int ret;
+ int i;
+ struct if_usb_devdata *pipe;
+
+ /* give it to probe, or global variable needed */
+ if_usb_ids[0].driver_info = (unsigned long)usb_ld;
+
+ for (i = 0; i < IF_USB_DEVNUM_MAX; i++) {
+ pipe = &usb_ld->devdata[i];
+ pipe->format = i;
+ pipe->disconnected = 1;
+ init_usb_anchor(&pipe->urbs);
+ init_usb_anchor(&pipe->reading);
+ }
+
+ init_waitqueue_head(&usb_ld->l2_wait);
+ init_usb_anchor(&usb_ld->deferred);
+
+ ret = usb_register(&if_usb_driver);
+ if (ret) {
+ mif_err("usb_register_driver() fail : %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+struct link_device *usb_create_link_device(void *data)
+{
+ int ret;
+ struct modem_data *pdata;
+ struct platform_device *pdev = (struct platform_device *)data;
+ struct usb_link_device *usb_ld = NULL;
+ struct link_device *ld = NULL;
+
+ pdata = pdev->dev.platform_data;
+
+ usb_ld = kzalloc(sizeof(struct usb_link_device), GFP_KERNEL);
+ if (!usb_ld)
+ goto err;
+
+ INIT_LIST_HEAD(&usb_ld->list_of_io_devices);
+ skb_queue_head_init(&usb_ld->ld.sk_fmt_tx_q);
+ skb_queue_head_init(&usb_ld->ld.sk_raw_tx_q);
+ spin_lock_init(&usb_ld->lock);
+
+ ld = &usb_ld->ld;
+ usb_ld->pdata = pdata;
+
+ ld->name = "usb";
+ ld->attach = usb_attach_io_dev;
+ ld->init_comm = usb_init_communication;
+ ld->terminate_comm = usb_terminate_communication;
+ ld->send = usb_send;
+ ld->com_state = COM_NONE;
+
+ /*ld->tx_wq = create_singlethread_workqueue("usb_tx_wq");*/
+ ld->tx_wq = alloc_workqueue("usb_tx_wq",
+ WQ_HIGHPRI | WQ_UNBOUND | WQ_RESCUER, 1);
+
+ if (!ld->tx_wq) {
+ mif_err("fail to create work Q.\n");
+ goto err;
+ }
+
+ usb_ld->pdata->irq_host_wakeup = gpio_to_irq(platform_get_irq(pdev, 1));
+ wake_lock_init(&usb_ld->gpiolock, WAKE_LOCK_SUSPEND,
+ "modem_usb_gpio_wake");
+ wake_lock_init(&usb_ld->susplock, WAKE_LOCK_SUSPEND,
+ "modem_usb_suspend_block");
+
+ INIT_DELAYED_WORK(&ld->tx_delayed_work, usb_tx_work);
+ INIT_DELAYED_WORK(&usb_ld->runtime_pm_work, runtime_pm_work);
+ /*
+ INIT_DELAYED_WORK(&usb_ld->post_resume_work, post_resume_work);
+ */
+ INIT_DELAYED_WORK(&usb_ld->wait_enumeration, wait_enumeration_work);
+ INIT_WORK(&usb_ld->disconnect_work, if_usb_force_disconnect);
+
+ /* create link pm device */
+ ret = link_pm_init(usb_ld, data);
+ if (ret)
+ goto err;
+
+ ret = if_usb_init(usb_ld);
+ if (ret)
+ goto err;
+
+ return ld;
+err:
+ if (ld && ld->tx_wq)
+ destroy_workqueue(ld->tx_wq);
+
+ kfree(usb_ld);
+
+ return NULL;
+}
+
+static struct usb_driver if_usb_driver = {
+ .name = "if_usb_driver",
+ .probe = if_usb_probe,
+ .disconnect = if_usb_disconnect,
+ .id_table = if_usb_ids,
+ .suspend = if_usb_suspend,
+ .resume = if_usb_resume,
+ .reset_resume = if_usb_reset_resume,
+ .supports_autosuspend = 1,
+};
+
+static void __exit if_usb_exit(void)
+{
+ usb_deregister(&if_usb_driver);
+}
+
+
+/* lte specific functions */
+
+static int lte_wake_resume(struct device *pdev)
+{
+ struct modem_data *pdata = pdev->platform_data;
+ int val;
+
+ val = gpio_get_value(pdata->gpio_host_wakeup);
+ if (!val) {
+ mif_debug("> S-WUP 1\n");
+ gpio_set_value(pdata->gpio_slave_wakeup, 1);
+ }
+
+ return 0;
+}
+
+static const struct dev_pm_ops lte_wake_pm_ops = {
+ .resume = lte_wake_resume,
+};
+
+static struct platform_driver lte_wake_driver = {
+ .driver = {
+ .name = "modem_lte_wake",
+ .pm = &lte_wake_pm_ops,
+ },
+};
+
+static int __init lte_wake_init(void)
+{
+ return platform_driver_register(&lte_wake_driver);
+}
+module_init(lte_wake_init);
diff --git a/drivers/misc/modem_if_na/modem_link_device_usb.h b/drivers/misc/modem_if_na/modem_link_device_usb.h
new file mode 100644
index 0000000..0db83b1
--- /dev/null
+++ b/drivers/misc/modem_if_na/modem_link_device_usb.h
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2010 Google, Inc.
+ * Copyright (C) 2010 Samsung Electronics.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __MODEM_LINK_DEVICE_USB_H__
+#define __MODEM_LINK_DEVICE_USB_H__
+
+#include <linux/usb.h>
+#include <linux/wakelock.h>
+
+#define IF_USB_DEVNUM_MAX 3
+
+#define IF_USB_FMT_EP 0
+#define IF_USB_RAW_EP 1
+#define IF_USB_RFS_EP 2
+
+#define AUTOSUSPEND_DELAY_MS 500
+#define HOST_WAKEUP_TIMEOUT_JIFFIES msecs_to_jiffies(500)
+#define WAIT_ENUMURATION_TIMEOUT_JIFFIES msecs_to_jiffies(30000)
+#define MAX_RETRY 3
+
+enum RESUME_STATUS {
+ CP_INITIATED_RESUME,
+ AP_INITIATED_RESUME,
+};
+
+enum {
+ EDC_MAX_ERRORS = 5,
+ EDC_ERROR_TIMEFRAME = HZ,
+};
+
+struct edc {
+ unsigned long timestart;
+ u16 errorcount;
+};
+
+static inline void edc_init(struct edc *edc)
+{
+ edc->timestart = jiffies;
+}
+
+static inline int edc_inc(struct edc *edc, u16 max_err, u16 timeframe)
+{
+ unsigned long now;
+
+ now = jiffies;
+ if (now - edc->timestart > timeframe) {
+ edc->errorcount = 1;
+ edc->timestart = now;
+ } else if (++edc->errorcount > max_err) {
+ edc->errorcount = 0;
+ edc->timestart = now;
+ return 1;
+ }
+ return 0;
+}
+
+struct if_usb_devdata {
+ struct usb_interface *data_intf;
+ unsigned int tx_pipe;
+ unsigned int rx_pipe;
+ u8 disconnected;
+
+ int format;
+ struct usb_anchor urbs;
+ struct usb_anchor reading;
+ unsigned int rx_buf_size;
+};
+
+struct usb_link_device {
+ /*COMMON LINK DEVICE*/
+ struct link_device ld;
+
+ struct modem_data *pdata;
+
+ /*USB SPECIFIC LINK DEVICE*/
+ struct usb_device *usbdev;
+ struct if_usb_devdata devdata[IF_USB_DEVNUM_MAX];
+ struct delayed_work runtime_pm_work;
+ struct delayed_work post_resume_work;
+ struct delayed_work wait_enumeration;
+ struct work_struct disconnect_work;
+
+ struct wake_lock gpiolock;
+ struct wake_lock susplock;
+
+ unsigned int dev_count;
+ unsigned int suspended;
+ atomic_t suspend_count;
+ enum RESUME_STATUS resume_status;
+ int if_usb_connected;
+ int flow_suspend;
+ int host_wake_timeout_flag;
+
+ unsigned gpio_slave_wakeup;
+ unsigned gpio_host_wakeup;
+ unsigned gpio_host_active;
+ int irq_host_wakeup;
+ struct delayed_work dwork;
+ struct work_struct resume_work;
+ int cpcrash_flag;
+ wait_queue_head_t l2_wait;
+
+ spinlock_t lock;
+ struct usb_anchor deferred;
+
+ /* LINK PM DEVICE DATA */
+ struct link_pm_data *link_pm_data;
+
+ /*COMMON LINK DEVICE*/
+ /* maybe -list of io devices for the link device to use */
+ /* to find where to send incoming packets to */
+ struct list_head list_of_io_devices;
+
+ struct edc urb_edc;
+};
+/* converts from struct link_device* to struct xxx_link_device* */
+#define to_usb_link_device(linkdev) \
+ container_of(linkdev, struct usb_link_device, ld)
+
+#define SET_SLAVE_WAKEUP(_pdata, _value) \
+do { \
+ gpio_set_value(_pdata->gpio_slave_wakeup, _value); \
+ mif_debug("> S-WUP %s\n", _value ? "1" : "0"); \
+} while (0)
+
+#define SET_HOST_ACTIVE(_pdata, _value) \
+do { \
+ gpio_set_value(_pdata->gpio_host_active, _value); \
+ mif_debug("> H-ACT %s\n", _value ? "1" : "0"); \
+} while (0)
+
+#define has_hub(usb_ld) ((usb_ld)->link_pm_data->has_usbhub)
+
+irqreturn_t usb_resume_irq(int irq, void *data);
+
+#endif
diff --git a/drivers/misc/modem_if_na/modem_link_pm_usb.c b/drivers/misc/modem_if_na/modem_link_pm_usb.c
new file mode 100644
index 0000000..4907de7
--- /dev/null
+++ b/drivers/misc/modem_if_na/modem_link_pm_usb.c
@@ -0,0 +1,370 @@
+/*
+ * Copyright (C) 2012 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.
+ *
+ */
+
+/*
+ * TODO (last updated Apr 13, 2012):
+ * - move usb hub feature to board file
+ * currently, some code was commented out by CONFIG_USBHUB_USB3503
+ * - Both usb_ld and link_pm_data have same gpio member.
+ * we have to remove it from one of them and
+ * make helper function for gpio handling.
+ */
+
+#define DEBUG
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/sched.h>
+#include <linux/irq.h>
+#include <linux/poll.h>
+#include <linux/gpio.h>
+#include <linux/if_arp.h>
+#include <linux/platform_device.h>
+#include <linux/suspend.h>
+
+#include "modem_link_pm_usb.h"
+
+bool link_pm_is_connected(struct usb_link_device *usb_ld)
+{
+#ifdef CONFIG_USBHUB_USB3503
+ if (has_hub(usb_ld)) {
+ if (usb_ld->link_pm_data->hub_status == HUB_STATE_RESUMMING
+ || usb_ld->link_pm_data->hub_init_lock)
+ return false;
+
+ if (usb_ld->link_pm_data->hub_status == HUB_STATE_OFF) {
+ schedule_delayed_work(
+ &usb_ld->link_pm_data->link_pm_hub, 0);
+ return false;
+ }
+ }
+#endif
+ if (!usb_ld->if_usb_connected) {
+ mif_err("if not connected\n");
+ return false;
+ }
+
+ return true;
+}
+
+#ifdef CONFIG_USBHUB_USB3503
+static void link_pm_hub_work(struct work_struct *work)
+{
+ int err;
+ struct link_pm_data *pm_data =
+ container_of(work, struct link_pm_data, link_pm_hub.work);
+
+ if (pm_data->hub_status == HUB_STATE_ACTIVE)
+ return;
+
+ if (!pm_data->port_enable) {
+ mif_err("mif: hub power func not assinged\n");
+ return;
+ }
+ wake_lock(&pm_data->hub_lock);
+
+ /* If kernel if suspend, wait the ehci resume */
+ if (pm_data->dpm_suspending) {
+ mif_info("dpm_suspending\n");
+ schedule_delayed_work(&pm_data->link_pm_hub,
+ msecs_to_jiffies(500));
+ goto exit;
+ }
+
+ switch (pm_data->hub_status) {
+ case HUB_STATE_OFF:
+ pm_data->hub_status = HUB_STATE_RESUMMING;
+ mif_trace("hub off->on\n");
+
+ /* 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);
+ if (err < 0) {
+ mif_err("hub on fail err=%d\n", err);
+ err = pm_data->port_enable(2, 0);
+ if (err < 0)
+ mif_err("hub off fail err=%d\n", err);
+ pm_data->hub_status = HUB_STATE_OFF;
+ if (pm_data->root_hub)
+ pm_runtime_put_sync(pm_data->root_hub);
+ goto exit;
+ }
+ /* resume root hub */
+ schedule_delayed_work(&pm_data->link_pm_hub,
+ msecs_to_jiffies(100));
+ break;
+ case HUB_STATE_RESUMMING:
+ if (pm_data->hub_on_retry_cnt++ > 50) {
+ pm_data->hub_on_retry_cnt = 0;
+ pm_data->hub_status = HUB_STATE_OFF;
+ if (pm_data->root_hub)
+ pm_runtime_put_sync(pm_data->root_hub);
+ }
+ mif_trace("hub resumming\n");
+ schedule_delayed_work(&pm_data->link_pm_hub,
+ msecs_to_jiffies(200));
+ break;
+ case HUB_STATE_PREACTIVE:
+ mif_trace("hub active\n");
+ pm_data->hub_on_retry_cnt = 0;
+ wake_unlock(&pm_data->hub_lock);
+ pm_data->hub_status = HUB_STATE_ACTIVE;
+ complete(&pm_data->hub_active);
+ if (pm_data->root_hub)
+ pm_runtime_put_sync(pm_data->root_hub);
+ break;
+ }
+exit:
+ return;
+}
+#endif
+
+static int link_pm_hub_standby(struct link_pm_data *pm_data)
+{
+ int err = 0;
+
+#ifdef CONFIG_USBHUB_USB3503
+ mif_info("wait hub standby\n");
+
+ if (!pm_data->port_enable) {
+ mif_err("hub power func not assinged\n");
+ return -ENODEV;
+ }
+
+ err = pm_data->port_enable(2, 0);
+ if (err < 0)
+ mif_err("hub off fail err=%d\n", err);
+
+ pm_data->hub_status = HUB_STATE_OFF;
+#endif
+ return err;
+}
+
+bool link_pm_set_active(struct usb_link_device *usb_ld)
+{
+ int ret;
+ struct link_pm_data *pm_data = usb_ld->link_pm_data;
+
+ if (has_hub(usb_ld)) {
+ if (pm_data->hub_status != HUB_STATE_ACTIVE) {
+ INIT_COMPLETION(pm_data->hub_active);
+ SET_SLAVE_WAKEUP(usb_ld->pdata, 1);
+ ret = wait_for_completion_timeout(&pm_data->hub_active,
+ msecs_to_jiffies(2000));
+ if (!ret) { /*timeout*/
+ mif_err("hub on timeout - retry\n");
+ SET_SLAVE_WAKEUP(usb_ld->pdata, 0);
+ queue_delayed_work(usb_ld->ld.tx_wq,
+ &usb_ld->ld.tx_delayed_work, 0);
+ return false;
+ }
+ }
+ } else {
+ /* TODO do something */
+ }
+ return true;
+}
+
+static long link_pm_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int value, err = 0;
+ struct task_struct *task = get_current();
+ struct link_pm_data *pm_data = file->private_data;
+ struct usb_link_device *usb_ld = pm_data->usb_ld;
+ char taskname[TASK_COMM_LEN];
+
+ mif_info("cmd: 0x%08x\n", cmd);
+
+ switch (cmd) {
+ case IOCTL_LINK_CONTROL_ACTIVE:
+ if (copy_from_user(&value, (const void __user *)arg,
+ sizeof(int)))
+ return -EFAULT;
+ gpio_set_value(pm_data->gpio_link_active, value);
+ break;
+ case IOCTL_LINK_GET_HOSTWAKE:
+ return !gpio_get_value(pm_data->gpio_link_hostwake);
+ case IOCTL_LINK_CONNECTED:
+ return pm_data->usb_ld->if_usb_connected;
+ case IOCTL_LINK_PORT_ON: /* hub only */
+ /* ignore cp host wakeup irq, set the hub_init_lock when AP try
+ CP off and release hub_init_lock when CP boot done */
+ pm_data->hub_init_lock = 0;
+ if (pm_data->root_hub) {
+ pm_runtime_resume(pm_data->root_hub);
+ pm_runtime_forbid(pm_data->root_hub->parent);
+ }
+ if (pm_data->port_enable) {
+ err = pm_data->port_enable(2, 1);
+ if (err < 0) {
+ mif_err("hub on fail err=%d\n", err);
+ goto exit;
+ }
+ pm_data->hub_status = HUB_STATE_RESUMMING;
+ }
+ break;
+ case IOCTL_LINK_PORT_OFF: /* hub only */
+ err = link_pm_hub_standby(pm_data);
+ if (err < 0) {
+ mif_err("usb3503 active fail\n");
+ goto exit;
+ }
+ pm_data->hub_init_lock = 1;
+ pm_data->hub_handshake_done = 0;
+
+ break;
+ case IOCTL_LINK_BLOCK_AUTOSUSPEND: /* block autosuspend forever */
+ mif_info("blocked autosuspend by `%s(%d)'\n",
+ get_task_comm(taskname, task), task->pid);
+ pm_data->block_autosuspend = true;
+ if (usb_ld->usbdev)
+ pm_runtime_forbid(&usb_ld->usbdev->dev);
+ break;
+ case IOCTL_LINK_ENABLE_AUTOSUSPEND: /* Enable autosuspend */
+ mif_info("autosuspend enabled by `%s(%d)'\n",
+ get_task_comm(taskname, task), task->pid);
+ pm_data->block_autosuspend = false;
+ if (usb_ld->usbdev)
+ pm_runtime_allow(&usb_ld->usbdev->dev);
+ else {
+ mif_err("Enable autosuspend failed\n");
+ err = -ENODEV;
+ }
+ break;
+ default:
+ break;
+ }
+exit:
+ return err;
+}
+
+static int link_pm_open(struct inode *inode, struct file *file)
+{
+ struct link_pm_data *pm_data =
+ (struct link_pm_data *)file->private_data;
+ file->private_data = (void *)pm_data;
+ return 0;
+}
+
+static int link_pm_release(struct inode *inode, struct file *file)
+{
+ file->private_data = NULL;
+ return 0;
+}
+
+static const struct file_operations link_pm_fops = {
+ .owner = THIS_MODULE,
+ .open = link_pm_open,
+ .release = link_pm_release,
+ .unlocked_ioctl = link_pm_ioctl,
+};
+
+static int link_pm_notifier_event(struct notifier_block *this,
+ unsigned long event, void *ptr)
+{
+ struct link_pm_data *pm_data =
+ container_of(this, struct link_pm_data, pm_notifier);
+
+ switch (event) {
+ case PM_SUSPEND_PREPARE:
+ pm_data->dpm_suspending = true;
+ link_pm_hub_standby(pm_data);
+ return NOTIFY_OK;
+ case PM_POST_SUSPEND:
+ pm_data->dpm_suspending = false;
+ return NOTIFY_OK;
+ }
+ return NOTIFY_DONE;
+}
+
+int link_pm_init(struct usb_link_device *usb_ld, void *data)
+{
+ int err;
+ int irq;
+ struct platform_device *pdev = (struct platform_device *)data;
+ struct modem_data *pdata =
+ (struct modem_data *)pdev->dev.platform_data;
+ struct modemlink_pm_data *pm_pdata = pdata->link_pm_data;
+ struct 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;
+ }
+ /* get link pm data from modemcontrol's platform data */
+ pm_data->gpio_link_active = pm_pdata->gpio_link_active;
+ pm_data->gpio_link_hostwake = pm_pdata->gpio_link_hostwake;
+ pm_data->gpio_link_slavewake = pm_pdata->gpio_link_slavewake;
+ pm_data->link_reconnect = pm_pdata->link_reconnect;
+ pm_data->port_enable = pm_pdata->port_enable;
+ pm_data->cpufreq_lock = pm_pdata->cpufreq_lock;
+ pm_data->cpufreq_unlock = pm_pdata->cpufreq_unlock;
+ pm_data->autosuspend_delay_ms = pm_pdata->autosuspend_delay_ms;
+ pm_data->block_autosuspend = false;
+
+ pm_data->usb_ld = usb_ld;
+ pm_data->link_pm_active = false;
+ usb_ld->link_pm_data = pm_data;
+
+ pm_data->miscdev.minor = MISC_DYNAMIC_MINOR;
+ pm_data->miscdev.name = "link_pm";
+ pm_data->miscdev.fops = &link_pm_fops;
+
+ err = misc_register(&pm_data->miscdev);
+ if (err < 0) {
+ mif_err("fail to register pm device(%d)\n", err);
+ goto err_misc_register;
+ }
+
+ pm_data->hub_init_lock = 1;
+ irq = gpio_to_irq(usb_ld->pdata->gpio_host_wakeup);
+ err = request_threaded_irq(irq, NULL, usb_resume_irq,
+ IRQF_TRIGGER_HIGH | IRQF_ONESHOT, "modem_usb_wake", usb_ld);
+ if (err) {
+ mif_err("Failed to allocate an interrupt(%d)\n", irq);
+ goto err_request_irq;
+ }
+ enable_irq_wake(irq);
+
+ pm_data->has_usbhub = pm_pdata->has_usbhub;
+
+#ifdef CONFIG_USBHUB_USB3503
+ if (has_hub(usb_ld)) {
+ init_completion(&pm_data->hub_active);
+ pm_data->hub_status = HUB_STATE_OFF;
+ pm_pdata->p_hub_status = &pm_data->hub_status;
+ pm_data->hub_handshake_done = 0;
+ pm_data->root_hub = NULL;
+ wake_lock_init(&pm_data->hub_lock, WAKE_LOCK_SUSPEND,
+ "modem_hub_enum_lock");
+ INIT_DELAYED_WORK(&pm_data->link_pm_hub, link_pm_hub_work);
+ }
+#endif
+
+ wake_lock_init(&pm_data->boot_wake, WAKE_LOCK_SUSPEND, "boot_usb");
+
+ pm_data->pm_notifier.notifier_call = link_pm_notifier_event;
+ register_pm_notifier(&pm_data->pm_notifier);
+
+ return 0;
+
+err_request_irq:
+ misc_deregister(&pm_data->miscdev);
+err_misc_register:
+ kfree(pm_data);
+ return err;
+}
diff --git a/drivers/misc/modem_if_na/modem_link_pm_usb.h b/drivers/misc/modem_if_na/modem_link_pm_usb.h
new file mode 100644
index 0000000..a4d4ea3
--- /dev/null
+++ b/drivers/misc/modem_if_na/modem_link_pm_usb.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2012 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_PM_USB_H__
+#define __MODEM_LINK_PM_USB_H__
+
+#include <linux/platform_data/modem_na.h>
+#include "modem_prj.h"
+#include "modem_link_device_usb.h"
+
+#define IOCTL_LINK_CONTROL_ENABLE _IO('o', 0x30)
+#define IOCTL_LINK_CONTROL_ACTIVE _IO('o', 0x31)
+#define IOCTL_LINK_GET_HOSTWAKE _IO('o', 0x32)
+#define IOCTL_LINK_CONNECTED _IO('o', 0x33)
+#define IOCTL_LINK_SET_BIAS_CLEAR _IO('o', 0x34)
+
+#define IOCTL_LINK_PORT_ON _IO('o', 0x35)
+#define IOCTL_LINK_PORT_OFF _IO('o', 0x36)
+#define IOCTL_LINK_BLOCK_AUTOSUSPEND _IO('o', 0x37)
+#define IOCTL_LINK_ENABLE_AUTOSUSPEND _IO('o', 0x38)
+
+enum hub_status {
+ HUB_STATE_OFF, /* usb3503 0ff*/
+ HUB_STATE_RESUMMING, /* usb3503 on, but enummerattion was not yet*/
+ HUB_STATE_PREACTIVE,
+ HUB_STATE_ACTIVE, /* hub and CMC221 enumerate */
+};
+
+struct link_pm_data {
+ struct miscdevice miscdev;
+ struct usb_link_device *usb_ld;
+ unsigned gpio_link_active;
+ unsigned gpio_link_hostwake;
+ unsigned gpio_link_slavewake;
+ int (*link_reconnect)(void);
+ int link_reconnect_cnt;
+
+ struct workqueue_struct *wq;
+ struct completion active_done;
+/*USB3503*/
+ struct completion hub_active;
+ int hub_status;
+ bool has_usbhub;
+ /* ignore hub on by host wakeup irq before cp power on*/
+ int hub_init_lock;
+ /* C1 stay disconnect status after send 'a', skip 'a' next enumeration*/
+ int hub_handshake_done;
+ struct wake_lock hub_lock;
+ struct delayed_work link_pm_hub;
+ int hub_on_retry_cnt;
+ struct device *root_hub;
+
+ struct delayed_work link_pm_work;
+ struct delayed_work link_pm_start;
+ struct delayed_work link_reconnect_work;
+ bool resume_requested;
+ bool link_pm_active;
+
+ struct wake_lock l2_wake;
+ struct wake_lock boot_wake;
+ struct notifier_block pm_notifier;
+ bool dpm_suspending;
+
+ int (*port_enable)(int, int);
+
+ int (*cpufreq_lock)(void);
+ int (*cpufreq_unlock)(void);
+
+ int autosuspend_delay_ms; /* if zero, the default value is used */
+ bool block_autosuspend;
+};
+
+bool link_pm_set_active(struct usb_link_device *usb_ld);
+bool link_pm_is_connected(struct usb_link_device *usb_ld);
+int link_pm_init(struct usb_link_device *usb_ld, void *data);
+
+#endif
diff --git a/drivers/misc/modem_if_na/modem_modemctl_device_cbp71.c b/drivers/misc/modem_if_na/modem_modemctl_device_cbp71.c
new file mode 100644
index 0000000..17f493c
--- /dev/null
+++ b/drivers/misc/modem_if_na/modem_modemctl_device_cbp71.c
@@ -0,0 +1,269 @@
+/* /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
+ * 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_data/modem_na.h>
+#include "modem_prj.h"
+#include "modem_link_device_dpram.h"
+
+#define PIF_TIMEOUT (180 * HZ)
+#define DPRAM_INIT_TIMEOUT (15 * HZ)
+
+
+static irqreturn_t phone_active_irq_handler(int irq, void *_mc)
+{
+ int phone_reset = 0;
+ int phone_active_value = 0;
+ int phone_state = 0;
+ struct modem_ctl *mc = (struct modem_ctl *)_mc;
+
+ if (!mc->gpio_cp_reset || !mc->gpio_phone_active) {
+ pr_err("MIF :<%s> no gpio data\n", __func__);
+ return IRQ_HANDLED;
+ }
+ phone_reset = gpio_get_value(mc->gpio_cp_reset);
+ phone_active_value = gpio_get_value(mc->gpio_phone_active);
+ pr_info("MIF : <%s>phone_active : %d\n", \
+ __func__, phone_active_value);
+ if (phone_reset && phone_active_value) {
+ phone_state = STATE_ONLINE;
+ if (mc->iod && mc->iod->modem_state_changed)
+ mc->iod->modem_state_changed(mc->iod, phone_state);
+ } else if (phone_reset && !phone_active_value) {
+ if (mc->phone_state == STATE_ONLINE) {
+ phone_state = STATE_CRASH_EXIT;
+ if (mc->iod && mc->iod->modem_state_changed)
+ mc->iod->modem_state_changed(mc->iod,
+ phone_state);
+ }
+ } else {
+ phone_state = STATE_OFFLINE;
+ if (mc->iod && mc->iod->modem_state_changed)
+ mc->iod->modem_state_changed(mc->iod, phone_state);
+ }
+
+ if (phone_active_value)
+ 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 int cbp71_on(struct modem_ctl *mc)
+{
+
+ int ret;
+
+ struct dpram_link_device *dpram_ld =
+ to_dpram_link_device(mc->iod->link);
+
+ pr_err("MIF : cbp71_on()\n");
+
+ if (!mc->gpio_cp_off || !mc->gpio_cp_reset || !mc->gpio_cp_on) {
+ pr_err("MIF : <%s>no gpio data\n", __func__);
+ return -ENXIO;
+ }
+ gpio_set_value(mc->gpio_cp_on, 1);
+ mdelay(10);
+ gpio_set_value(mc->gpio_cp_reset, GPIO_LEVEL_LOW);
+ gpio_set_value(mc->gpio_cp_off, GPIO_LEVEL_LOW);
+ mdelay(600);
+ gpio_set_value(mc->gpio_cp_reset, GPIO_LEVEL_HIGH);
+ gpio_set_value(mc->gpio_cp_on, GPIO_LEVEL_HIGH);
+
+
+ mc->iod->modem_state_changed(mc->iod, STATE_BOOTING);
+
+ /* Wait here until the PHONE is up.
+ * Waiting as the this called from IOCTL->UM thread */
+ pr_debug("MIF : power control waiting for INT_MASK_CMD_PIF_INIT_DONE\n");
+
+ mc->clear_intr();
+
+ msleep(100);
+
+ gpio_set_value(mc->gpio_pda_active, 1);
+ printk(KERN_INFO "MIF : PDA_ACTIVE sets high.\n");
+
+ ret = wait_for_completion_interruptible_timeout(
+ &dpram_ld->dpram_init_cmd, DPRAM_INIT_TIMEOUT);
+ if (!ret) {
+ /* ret will be 0 on timeout, < zero if interrupted */
+ pr_warn("MIF : INIT_START cmd was not arrived.\n");
+ pr_warn("init_cmd_wait_condition is 0 and wait timeout happend\n");
+ return -ENXIO;
+ }
+
+ ret = wait_for_completion_interruptible_timeout(
+ &dpram_ld->modem_pif_init_done, PIF_TIMEOUT);
+ if (!ret) {
+ pr_warn("MIF : PIF init failed\n");
+ pr_warn("pif_init_wait_condition is 0 and wait timeout happend\n");
+ return -ENXIO;
+ }
+
+ pr_debug("MIF : complete cbp71_on\n");
+
+ mc->iod->modem_state_changed(mc->iod, STATE_ONLINE);
+
+ return 0;
+}
+
+static int cbp71_off(struct modem_ctl *mc)
+{
+ int phone_wait_cnt = 0;
+ pr_err("MIF : cbp71_off()\n");
+
+ if (!mc->gpio_cp_off || !mc->gpio_cp_reset || !mc->gpio_phone_active) {
+ pr_err("MIF : no gpio data\n");
+ return -ENXIO;
+ }
+
+ /* confirm phone off */
+ while (1) {
+ if (gpio_get_value(mc->gpio_phone_active)) {
+ if (phone_wait_cnt > 5) {
+ pr_info("MIF:<%s> Try to Turn Phone Off(%d)\n",
+ __func__,
+ gpio_get_value(mc->gpio_phone_active));
+ gpio_set_value(mc->gpio_cp_reset, 0);
+ gpio_set_value(mc->gpio_cp_off, \
+ 0);
+ }
+ if (phone_wait_cnt > 7) {
+ pr_err("MIF:<%s> PHONE OFF Failed\n", __func__);
+ break;
+ }
+ phone_wait_cnt++;
+ msleep(100);
+ } else {
+ pr_info("MIF:<%s> PHONE OFF Success\n", __func__);
+ break;
+ }
+ }
+
+ /* set VIA off again */
+ gpio_set_value(mc->gpio_cp_reset, 0);
+ gpio_set_value(mc->gpio_cp_off, 0);
+
+ mc->iod->modem_state_changed(mc->iod, STATE_OFFLINE);
+
+ return 0;
+}
+
+static int cbp71_reset(struct modem_ctl *mc)
+{
+ int ret = 0;
+
+ pr_debug("MIF : cbp71_reset()\n");
+
+ ret = cbp71_off(mc);
+ if (ret)
+ return -ENXIO;
+
+ msleep(100);
+
+ ret = cbp71_on(mc);
+ if (ret)
+ return -ENXIO;
+
+ return 0;
+}
+
+static int cbp71_boot_on(struct modem_ctl *mc)
+{
+ pr_debug("MIF : cbp71_boot_on()\n");
+
+ if (!mc->gpio_cp_reset) {
+ pr_err("MIF : no gpio data\n");
+ return -ENXIO;
+ }
+ gpio_set_value(mc->gpio_cp_reset, 0);
+ msleep(600);
+ gpio_set_value(mc->gpio_cp_reset, 1);
+
+ mc->iod->modem_state_changed(mc->iod, STATE_BOOTING);
+
+ return 0;
+}
+
+static int cbp71_boot_off(struct modem_ctl *mc)
+{
+ pr_debug("MIF : cbp71_boot_off()\n");
+ return 0;
+}
+
+static void cbp71_get_ops(struct modem_ctl *mc)
+{
+ mc->ops.modem_on = cbp71_on;
+ mc->ops.modem_off = cbp71_off;
+ mc->ops.modem_reset = cbp71_reset;
+ mc->ops.modem_boot_on = cbp71_boot_on;
+ mc->ops.modem_boot_off = cbp71_boot_off;
+}
+
+int cbp71_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_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_cp_off = pdata->gpio_cp_off;
+
+#ifdef CONFIG_INTERNAL_MODEM_IF
+ mc->clear_intr = pdata->clear_intr;
+#endif
+ 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);
+
+ cbp71_get_ops(mc);
+ 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_na/modem_modemctl_device_cmc220.c b/drivers/misc/modem_if_na/modem_modemctl_device_cmc220.c
new file mode 100644
index 0000000..48639b8
--- /dev/null
+++ b/drivers/misc/modem_if_na/modem_modemctl_device_cmc220.c
@@ -0,0 +1,254 @@
+/* /linux/drivers/misc/modem_if/modem_modemctl_device_cmc220.c
+ *
+ * Copyright (C) 2010 Google, Inc.
+ * Copyright (C) 2010 Samsung Electronics.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/init.h>
+
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+
+#include <linux/platform_data/modem_na.h>
+#include "modem_prj.h"
+#include "modem_link_device_usb.h"
+
+static int cmc220_on(struct modem_ctl *mc)
+{
+ pr_info("[MODEM_IF] %s()\n", __func__);
+
+ if (!mc->gpio_cp_off || !mc->gpio_cp_on || !mc->gpio_cp_reset) {
+ pr_err("[MODEM_IF] no gpio data\n");
+ return -ENXIO;
+ }
+
+ gpio_set_value(mc->gpio_cp_on, 1);
+ msleep(300);
+ gpio_set_value(mc->gpio_cp_reset, 1);
+ msleep(100);
+ gpio_set_value(mc->gpio_cp_off, 0);
+ msleep(300);
+ mc->phone_state = STATE_BOOTING;
+ return 0;
+}
+
+static int cmc220_off(struct modem_ctl *mc)
+{
+ pr_info("[MODEM_IF] %s()\n", __func__);
+
+ if (!mc->gpio_cp_off || !mc->gpio_cp_on || !mc->gpio_cp_reset) {
+ pr_err("[MODEM_IF] no gpio data\n");
+ return -ENXIO;
+ }
+
+ gpio_set_value(mc->gpio_cp_on, 0);
+ msleep(100);
+ gpio_set_value(mc->gpio_cp_off, 1);
+ msleep(100);
+ gpio_set_value(mc->gpio_cp_reset, 0);
+
+ mc->phone_state = STATE_OFFLINE;
+
+ return 0;
+}
+
+static int cmc220_force_crash_exit(struct modem_ctl *mc)
+{
+ pr_info("[MODEM_IF] %s: # %d\n", __func__, ++(mc->crash_cnt));
+
+ mc->phone_state = STATE_CRASH_EXIT;/* DUMP START */
+
+ if (mc->iod && mc->iod->modem_state_changed)
+ mc->iod->modem_state_changed(mc->iod, mc->phone_state);
+
+ return 0;
+}
+
+static int cmc220_dump_reset(struct modem_ctl *mc)
+{
+ pr_info("[MODEM_IF] %s()\n", __func__);
+
+ if (!mc->gpio_cp_reset)
+ return -ENXIO;
+
+ gpio_set_value(mc->gpio_host_active, 0);
+ mc->cpcrash_flag = 1;
+
+ gpio_set_value(mc->gpio_cp_reset, 0);
+ msleep(100);
+ gpio_set_value(mc->gpio_cp_reset, 1);
+ msleep(300);
+
+ mc->phone_state = STATE_BOOTING;
+
+ return 0;
+}
+
+static int cmc220_reset(struct modem_ctl *mc)
+{
+ pr_info("[MODEM_IF] %s()\n", __func__);
+
+ if (!mc->gpio_cp_reset)
+ return -ENXIO;
+
+ if (cmc220_off(mc))
+ return -ENXIO;
+ msleep(100);
+ if (cmc220_on(mc))
+ return -ENXIO;
+
+ mc->phone_state = STATE_BOOTING;
+
+ return 0;
+}
+
+static int cmc220_boot_on(struct modem_ctl *mc)
+{
+ pr_info("[MODEM_IF] %s()\n", __func__);
+ return 0;
+}
+
+static int cmc220_boot_off(struct modem_ctl *mc)
+{
+ pr_info("[MODEM_IF] %s()\n", __func__);
+ return 0;
+}
+
+static int cmc220_get_active(struct modem_ctl *mc)
+{
+ if (!mc->gpio_phone_active || !mc->gpio_cp_reset)
+ return -ENXIO;
+
+ pr_debug("cp %d phone %d\n",
+ gpio_get_value(mc->gpio_cp_reset),
+ gpio_get_value(mc->gpio_phone_active));
+
+ if (gpio_get_value(mc->gpio_cp_reset))
+ return gpio_get_value(mc->gpio_phone_active);
+
+ return 0;
+}
+
+
+static void mc_work(struct work_struct *work_arg)
+{
+
+ struct modem_ctl *mc = container_of(work_arg, struct modem_ctl,
+ dwork.work);
+
+ int phone_active;
+
+ phone_active = cmc220_get_active(mc);
+ if (phone_active < 0) {
+ pr_err("[MODEM_IF] gpio not initialized\n");
+ return;
+ }
+
+ switch (mc->phone_state) {
+ case STATE_CRASH_EXIT:
+ case STATE_BOOTING:
+ case STATE_LOADER_DONE:
+ if (phone_active) {
+ if (mc->cpcrash_flag) {
+ pr_info("[MODEM_IF] LTE DUMP END!!\n");
+ mc->cpcrash_flag = 0;
+ }
+ }
+ break;
+ case STATE_ONLINE:
+ if (!phone_active) {
+ pr_info("[MODEM_IF] LTE CRASHED!! LTE DUMP START!!\n");
+ mc->phone_state = STATE_CRASH_EXIT;
+ if (mc->iod && mc->iod->modem_state_changed)
+ mc->iod->modem_state_changed(mc->iod,
+ mc->phone_state);
+ }
+ break;
+ default:
+ mc->phone_state = STATE_OFFLINE;
+ pr_err("[MODEM_IF], phone_status changed to invalid!!\n");
+ break;
+ }
+}
+
+
+
+static irqreturn_t phone_active_irq_handler(int irq, void *_mc)
+{
+ struct modem_ctl *mc = (struct modem_ctl *)_mc;
+
+ schedule_delayed_work(&mc->dwork, 20);
+
+ return IRQ_HANDLED;
+}
+
+static void cmc220_get_ops(struct modem_ctl *mc)
+{
+ mc->ops.modem_on = cmc220_on;
+ mc->ops.modem_off = cmc220_off;
+ mc->ops.modem_reset = cmc220_reset;
+ mc->ops.modem_boot_on = cmc220_boot_on;
+ mc->ops.modem_boot_off = cmc220_boot_off;
+ mc->ops.modem_force_crash_exit = cmc220_force_crash_exit;
+ mc->ops.modem_dump_reset = cmc220_dump_reset;
+}
+
+int cmc220_init_modemctl_device(struct modem_ctl *mc,
+ struct modem_data *pdata)
+{
+ int ret = 0;
+ struct platform_device *pdev;
+
+ mc->gpio_cp_on = pdata->gpio_cp_on;
+ mc->gpio_reset_req_n = pdata->gpio_reset_req_n;
+ mc->gpio_cp_reset = pdata->gpio_cp_reset;
+ mc->gpio_pda_active = pdata->gpio_pda_active;
+ mc->gpio_phone_active = pdata->gpio_phone_active;
+ mc->gpio_cp_dump_int = pdata->gpio_cp_dump_int;
+ mc->gpio_flm_uart_sel = pdata->gpio_flm_uart_sel;
+ mc->gpio_cp_warm_reset = pdata->gpio_cp_warm_reset;
+ mc->gpio_cp_off = pdata->gpio_cp_off;
+ mc->gpio_slave_wakeup = pdata->gpio_slave_wakeup;
+ mc->gpio_host_active = pdata->gpio_host_active;
+ mc->gpio_host_wakeup = pdata->gpio_host_wakeup;
+
+ pdev = to_platform_device(mc->dev);
+ mc->irq_phone_active = gpio_to_irq(mc->gpio_phone_active);
+
+ cmc220_get_ops(mc);
+
+ dev_set_drvdata(mc->dev, mc);
+
+ INIT_DELAYED_WORK(&mc->dwork, mc_work);
+
+ ret = request_irq(mc->irq_phone_active, phone_active_irq_handler,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ "lte_phone_active", mc);
+ if (ret) {
+ pr_err("[MODEM_IF] Failed to allocate an interrupt(%d)\n",
+ mc->irq_phone_active);
+ goto irq_fail;
+ }
+ mc->irq[0] = mc->irq_phone_active;
+ enable_irq_wake(mc->irq_phone_active);
+ /*disable_irq(mc->irq_phone_active);*/
+
+ return ret;
+
+irq_fail:
+ kfree(mc);
+ return ret;
+}
diff --git a/drivers/misc/modem_if_na/modem_net_flowcontrol_device.c b/drivers/misc/modem_if_na/modem_net_flowcontrol_device.c
new file mode 100644
index 0000000..6660079
--- /dev/null
+++ b/drivers/misc/modem_if_na/modem_net_flowcontrol_device.c
@@ -0,0 +1,115 @@
+/* /linux/drivers/misc/modem_if/modem_net_flowcontrol_device.c
+ *
+ * Copyright (C) 2011 Google, Inc.
+ * 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/kernel.h>
+#include <linux/fs.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/sched.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/platform_data/modem_na.h>
+
+#include "modem_prj.h"
+
+
+#define NET_FLOWCONTROL_DEV_NAME_LEN 8
+
+static int modem_net_flowcontrol_device_open(
+ struct inode *inode, struct file *filp)
+{
+ return 0;
+}
+
+static int modem_net_flowcontrol_device_release(
+ struct inode *inode, struct file *filp)
+{
+ return 0;
+}
+
+static long modem_net_flowcontrol_device_ioctl(
+ struct file *filp, unsigned int cmd, unsigned long arg)
+{
+ struct net *this_net;
+ struct net_device *ndev;
+ char dev_name[NET_FLOWCONTROL_DEV_NAME_LEN];
+ u8 chan;
+
+ if (copy_from_user(&chan, (void __user *)arg, sizeof(char)))
+ return -EFAULT;
+
+ if (chan > 15)
+ return -ENODEV;
+
+ snprintf(dev_name, NET_FLOWCONTROL_DEV_NAME_LEN, "rmnet%d", (int)chan);
+ this_net = get_net_ns_by_pid(current->pid);
+ ndev = __dev_get_by_name(this_net, dev_name);
+ if (ndev == NULL) {
+ pr_err("[MODEM_IF] %s: device = %s not exist\n", __func__,
+ dev_name);
+ return -ENODEV;
+ }
+
+ switch (cmd) {
+ case IOCTL_MODEM_NET_SUSPEND:
+ netif_stop_queue(ndev);
+ pr_info("[MODEM_IF] NET SUSPEND(%s)\n", dev_name);
+ break;
+ case IOCTL_MODEM_NET_RESUME:
+ netif_wake_queue(ndev);
+ pr_info("[MODEM_IF] NET RESUME(%s)\n", dev_name);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static const struct file_operations modem_net_flowcontrol_device_fops = {
+ .owner = THIS_MODULE,
+ .open = modem_net_flowcontrol_device_open,
+ .release = modem_net_flowcontrol_device_release,
+ .unlocked_ioctl = modem_net_flowcontrol_device_ioctl,
+};
+
+static int __init modem_net_flowcontrol_device_init(void)
+{
+ int ret = 0;
+ struct io_device *net_flowcontrol_dev;
+
+ net_flowcontrol_dev = kzalloc(sizeof(struct io_device), GFP_KERNEL);
+ if (!net_flowcontrol_dev) {
+ pr_err("[MODEM_IF] net_flowcontrol_dev io device memory alloc fail\n");
+ return -ENOMEM;
+ }
+
+ net_flowcontrol_dev->miscdev.minor = MISC_DYNAMIC_MINOR;
+ net_flowcontrol_dev->miscdev.name = "modem_br";
+ net_flowcontrol_dev->miscdev.fops = &modem_net_flowcontrol_device_fops;
+
+ ret = misc_register(&net_flowcontrol_dev->miscdev);
+ if (ret)
+ pr_err("[MODEM_IF] failed to register misc br device : %s\n",
+ net_flowcontrol_dev->miscdev.name);
+
+ return ret;
+}
+
+module_init(modem_net_flowcontrol_device_init);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Samsung Modem IF Net Flowcontrol Driver");
diff --git a/drivers/misc/modem_if_na/modem_prj.h b/drivers/misc/modem_if_na/modem_prj.h
new file mode 100644
index 0000000..e5f27d8
--- /dev/null
+++ b/drivers/misc/modem_if_na/modem_prj.h
@@ -0,0 +1,239 @@
+/*
+ * Copyright (C) 2010 Google, Inc.
+ * Copyright (C) 2010 Samsung Electronics.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __MODEM_PRJ_H__
+#define __MODEM_PRJ_H__
+
+#include <linux/wait.h>
+#include <linux/miscdevice.h>
+#include <linux/skbuff.h>
+
+
+#define MAX_LINK_DEVTYPE 3
+#define MAX_RAW_DEVS 32
+#define MAX_NUM_IO_DEV (MAX_RAW_DEVS + 4)
+
+#define IOCTL_MODEM_ON _IO('o', 0x19)
+#define IOCTL_MODEM_OFF _IO('o', 0x20)
+#define IOCTL_MODEM_RESET _IO('o', 0x21)
+#define IOCTL_MODEM_BOOT_ON _IO('o', 0x22)
+#define IOCTL_MODEM_BOOT_OFF _IO('o', 0x23)
+#define IOCTL_MODEM_START _IO('o', 0x24)
+
+#define IOCTL_MODEM_SEND _IO('o', 0x25)
+#define IOCTL_MODEM_RECV _IO('o', 0x26)
+
+#define IOCTL_MODEM_STATUS _IO('o', 0x27)
+#define IOCTL_MODEM_GOTA_START _IO('o', 0x28)
+#define IOCTL_MODEM_FW_UPDATE _IO('o', 0x29)
+
+#define IOCTL_MODEM_NET_SUSPEND _IO('o', 0x30)
+#define IOCTL_MODEM_NET_RESUME _IO('o', 0x31)
+#define IOCTL_MODEM_DUMP_START _IO('o', 0x32)
+#define IOCTL_MODEM_DUMP_UPDATE _IO('o', 0x33)
+#define IOCTL_MODEM_FORCE_CRASH_EXIT _IO('o', 0x34)
+#define IOCTL_MODEM_DUMP_RESET _IO('o', 0x35)
+
+#define IPC_HEADER_MAX_SIZE 6 /* fmt 3, raw 6, rfs 6 */
+
+#define PSD_DATA_CHID_BEGIN 0x2A
+#define PSD_DATA_CHID_END 0x38
+
+#define IP6VERSION 6
+
+#define SOURCE_MAC_ADDR {0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC}
+
+/* Does modem ctl structure will use state ? or status defined below ?*/
+/* Be careful!! below sequence shouldn't be changed*/
+enum modem_state {
+ STATE_OFFLINE,
+ __UNUSED__,
+ STATE_CRASH_EXIT,
+ STATE_BOOTING,
+ STATE_ONLINE,
+ STATE_NV_REBUILDING,
+ STATE_LOADER_DONE,
+};
+
+enum {
+ COM_NONE,
+ COM_ONLINE,
+ COM_HANDSHAKE,
+ COM_BOOT,
+ COM_CRASH,
+ COM_BOOT_EBL,
+};
+
+struct header_data {
+ char hdr[IPC_HEADER_MAX_SIZE];
+ unsigned len;
+ unsigned flag_len;
+ char start; /*hdlc start header 0x7F*/
+};
+
+/* buffer type for modem image */
+struct dpram_firmware {
+ char *firmware;
+ int size;
+ int is_delta;
+};
+
+
+struct vnet {
+ struct io_device *iod;
+};
+
+struct io_device {
+ struct list_head list;
+ char *name;
+
+ wait_queue_head_t wq;
+
+ struct miscdevice miscdev;
+ struct net_device *ndev;
+
+ /* ID and Format for channel on the link */
+ unsigned id;
+ enum dev_format format;
+ enum modem_io io_typ;
+ enum modem_network net_typ;
+
+ struct sk_buff_head sk_rx_q;
+
+ /* work for each io device, when delayed work needed
+ * use this for private io device rx action
+ */
+ struct delayed_work rx_work;
+
+ /* for fragmentation data from link device */
+ struct sk_buff *skb_recv;
+ struct header_data h_data;
+
+ /* called from linkdevice when a packet arrives for this iodevice */
+ int (*recv)(struct io_device *iod, const char *data, unsigned int len);
+
+ /* inform the IO device that the modem is now online or offline or
+ * crashing or whatever...
+ */
+ void (*modem_state_changed)(struct io_device *iod, enum modem_state);
+
+ struct link_device *link;
+ struct modem_ctl *mc;
+
+ void *private_data;
+};
+#define to_io_device(misc) container_of(misc, struct io_device, miscdev)
+
+struct io_raw_devices {
+ struct io_device *raw_devices[MAX_RAW_DEVS];
+ int num_of_raw_devs;
+};
+
+struct link_device {
+ char *name;
+
+ struct sk_buff_head sk_fmt_tx_q;
+ struct sk_buff_head sk_raw_tx_q;
+
+ struct workqueue_struct *tx_wq;
+ struct workqueue_struct *tx_raw_wq;
+ struct work_struct tx_work;
+ struct delayed_work tx_delayed_work;
+
+ unsigned com_state;
+
+ /* called during init to associate an io device with this link */
+ int (*attach)(struct link_device *ld, struct io_device *iod);
+
+ /* init communication - setting link driver */
+ int (*init_comm)(struct link_device *ld, struct io_device *iod);
+ /* terminate communication */
+ void (*terminate_comm)(struct link_device *ld, struct io_device *iod);
+
+ /* called by an io_device when it has a packet to send over link
+ * - the io device is passed so the link device can look at id and
+ * format fields to determine how to route/format the packet
+ */
+ int (*send)(struct link_device *ld, struct io_device *iod,
+ struct sk_buff *skb);
+
+ int (*gota_start)(struct link_device *ld, struct io_device *iod);
+ int (*dump_start)(struct link_device *ld, struct io_device *iod);
+
+ int (*modem_update)(
+ struct link_device *ld,
+ struct io_device *iod,
+ unsigned long arg);
+ int (*dump_update)(
+ struct link_device *ld,
+ struct io_device *iod,
+ unsigned long arg);
+};
+
+struct modemctl_ops {
+ int (*modem_on) (struct modem_ctl *);
+ int (*modem_off) (struct modem_ctl *);
+ int (*modem_reset) (struct modem_ctl *);
+ int (*modem_boot_on) (struct modem_ctl *);
+ int (*modem_boot_off) (struct modem_ctl *);
+ int (*modem_force_crash_exit) (struct modem_ctl *);
+ int (*modem_dump_reset) (struct modem_ctl *);
+};
+
+struct modem_ctl {
+ struct device *dev;
+ char *name;
+
+ int phone_state;
+
+ unsigned gpio_cp_on;
+ unsigned gpio_reset_req_n;
+ unsigned gpio_cp_reset;
+ unsigned gpio_pda_active;
+ unsigned gpio_phone_active;
+ unsigned gpio_cp_dump_int;
+ unsigned gpio_flm_uart_sel;
+ unsigned gpio_cp_warm_reset;
+ unsigned gpio_cp_off;
+
+ int irq_phone_active;
+
+ struct work_struct work;
+
+#if defined(CONFIG_LTE_MODEM_CMC221) || defined(CONFIG_LTE_MODEM_CMC220)
+ 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;
+ struct work_struct resume_work;
+ int wakeup_flag; /*flag for CP boot GPIO sync flag*/
+ int cpcrash_flag;
+ int crash_cnt;
+ struct completion *l2_done;
+ int irq[3];
+#endif /*CONFIG_LTE_MODEM_CMC221*/
+
+#ifdef CONFIG_INTERNAL_MODEM_IF
+ void (*clear_intr)(void);
+#endif
+ struct modemctl_ops ops;
+ struct io_device *iod;
+};
+
+int init_io_device(struct io_device *iod);
+
+#endif
diff --git a/drivers/misc/modem_if_na/modem_utils.c b/drivers/misc/modem_if_na/modem_utils.c
new file mode 100644
index 0000000..594b7b0
--- /dev/null
+++ b/drivers/misc/modem_if_na/modem_utils.c
@@ -0,0 +1,391 @@
+/*
+ * 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/netdevice.h>
+#include <linux/platform_data/modem_na.h>
+#include <linux/platform_device.h>
+#include <linux/skbuff.h>
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#include <linux/udp.h>
+#include <net/ip.h>
+
+#include "modem_prj.h"
+#include "modem_utils.h"
+
+#define CMD_SUSPEND ((unsigned short)(0x00CA))
+#define CMD_RESUME ((unsigned short)(0x00CB))
+
+static char ip_flags[16];
+static char tcp_flags[32];
+
+
+/* dump2hex
+ * dump data to hex as fast as possible.
+ * the length of @buf 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 const char *hex = "0123456789abcdef";
+ char *dest = buf;
+ int i;
+
+ for (i = 0; i < len; i++) {
+ *dest++ = hex[(data[i] >> 4) & 0xf];
+ *dest++ = hex[data[i] & 0xf];
+ *dest++ = ' ';
+ }
+ if (likely(len > 0))
+ dest--; /* last space will be overwrited with null */
+
+ *dest = '\0';
+
+ return dest - buf;
+}
+
+/* print buffer as hex string */
+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);
+ return printk(KERN_DEBUG "[MIF] %s(%u): %s%s\n", tag, data_len, hexstr,
+ len == data_len ? "" : " ...");
+}
+
+/* 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)
+{
+ unsigned short *cmd, *end = (unsigned short *)(data + len);
+ struct io_device *iod = NULL, *multi_raw_iod;
+ int i;
+
+ pr_debug("[MODEM_IF] flow control cmd: size=%d\n", len);
+
+ multi_raw_iod = find_iodev(ld, IPC_MULTI_RAW);
+ if (!multi_raw_iod || !multi_raw_iod->private_data) {
+ pr_err("[MODEM_IF] %s: no multi raw device\n", __func__);
+ return -ENODEV;
+ }
+
+ for (cmd = (unsigned short *)data; cmd < end; cmd++) {
+ switch (*cmd) {
+ case CMD_SUSPEND:
+ raw_devs_for_each(multi_raw_iod, i, iod) {
+ if (iod->io_typ == IODEV_NET && iod->ndev)
+ netif_stop_queue(iod->ndev);
+ }
+ ld->raw_tx_suspended = true;
+ pr_info("[MODEM_IF] flowctl CMD_SUSPEND(%04X)\n", *cmd);
+ break;
+
+ case CMD_RESUME:
+ raw_devs_for_each(multi_raw_iod, i, iod) {
+ if (iod->io_typ == IODEV_NET && iod->ndev)
+ netif_wake_queue(iod->ndev);
+ }
+ ld->raw_tx_suspended = false;
+ complete_all(&ld->raw_tx_resumed_by_cp);
+ pr_info("[MODEM_IF] flowctl CMD_RESUME(%04X)\n", *cmd);
+ break;
+
+ default:
+ pr_err("[MODEM_IF] flowctl BAD CMD: %04X\n", *cmd);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+void mif_print_data(char *buf, int len)
+{
+ int words = len >> 4;
+ int residue = len - (words << 4);
+ int i;
+ char *b;
+ char last[80];
+ char tb[8];
+
+ /* 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));
+ for (i = 0; i < residue; i++) {
+ sprintf(tb, "%02x ", b[i]);
+ strcat(last, tb);
+ if ((i & 0x3) == 0x3) {
+ sprintf(tb, " ");
+ strcat(last, tb);
+ }
+ }
+ }
+
+ for (i = 0; i < words; i++) {
+ b = buf + (i << 4);
+ printk(KERN_ERR "%04X: "
+ "%02x %02x %02x %02x %02x %02x %02x %02x "
+ "%02x %02x %02x %02x %02x %02x %02x %02x\n",
+ (i << 4),
+ b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7],
+ b[8], b[9], b[10], b[11], b[12], b[13], b[14], b[15]);
+ }
+
+ /* Print the last line */
+ if (residue > 0)
+ printk(KERN_ERR "%s\n", last);
+}
+
+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);
+ u8 *data;
+ int dlen;
+
+ /* Actual HDLC header starts from after START flag (0x7F) */
+ frm = (u8 *)(psrc + 1);
+
+ /* Point HDLC header and IPC header */
+ hh = (struct fmt_hdr *)(frm);
+ fh = (struct sipc_fmt_hdr *)(frm + hh_len);
+
+ /* Point IPC data */
+ data = frm + (hh_len + fh_len);
+ dlen = hh->len - (hh_len + fh_len);
+
+ pr_err("--------------------HDLC & FMT HEADER----------------------\n");
+
+ pr_err("HDLC len = %d, HDLC control = 0x%02x\n", hh->len, hh->control);
+
+ pr_err("(M)0x%02X, (S)0x%02X, (T)0x%02X, mseq:%d, aseq:%d, len:%d\n",
+ fh->main_cmd, fh->sub_cmd, fh->cmd_type,
+ fh->msg_seq, fh->ack_seq, fh->len);
+
+ pr_err("-----------------------IPC FMT DATA------------------------\n");
+
+ if (dlen > 0) {
+ if (dlen > 64)
+ dlen = 64;
+ mif_print_data(data, dlen);
+ }
+
+ pr_err("-----------------------------------------------------------\n");
+}
+
+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);
+ u8 *data;
+ int dlen;
+
+ /* Point IPC data */
+ data = (u8 *)(psrc + fh_len);
+ dlen = fh->len - fh_len;
+
+ pr_err("----------------------IPC FMT HEADER-----------------------\n");
+
+ pr_err("(M)0x%02X, (S)0x%02X, (T)0x%02X, mseq:%d, aseq:%d, len:%d\n",
+ fh->main_cmd, fh->sub_cmd, fh->cmd_type,
+ fh->msg_seq, fh->ack_seq, fh->len);
+
+ pr_err("-----------------------IPC FMT DATA------------------------\n");
+
+ if (dlen > 0)
+ mif_print_data(data, dlen);
+
+ pr_err("-----------------------------------------------------------\n");
+}
+
+static void print_tcp_header(u8 *pkt)
+{
+ int i;
+ struct tcphdr *tcph = (struct tcphdr *)pkt;
+ u8 *opt = pkt + TCP_HDR_SIZE;
+ unsigned opt_len = (tcph->doff << 2) - TCP_HDR_SIZE;
+
+/*-------------------------------------------------------------------------
+
+ TCP Header Format
+
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Source Port | Destination Port |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Sequence Number |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Acknowledgment Number |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Data | |C|E|U|A|P|R|S|F| |
+ | Offset| Rsvd |W|C|R|C|S|S|Y|I| Window |
+ | | |R|E|G|K|H|T|N|N| |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Checksum | Urgent Pointer |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Options | Padding |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | data |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+-------------------------------------------------------------------------*/
+
+ memset(tcp_flags, 0, sizeof(tcp_flags));
+ if (tcph->cwr)
+ strcat(tcp_flags, "CWR ");
+ if (tcph->ece)
+ strcat(tcp_flags, "ECE ");
+ if (tcph->urg)
+ strcat(tcp_flags, "URG ");
+ if (tcph->ack)
+ strcat(tcp_flags, "ACK ");
+ if (tcph->psh)
+ strcat(tcp_flags, "PSH ");
+ if (tcph->rst)
+ strcat(tcp_flags, "RST ");
+ if (tcph->syn)
+ strcat(tcp_flags, "SYN ");
+ if (tcph->fin)
+ strcat(tcp_flags, "FIN ");
+
+ pr_err("TCP:: Src Port = %u, Dst Port = %u\n",
+ ntohs(tcph->source), ntohs(tcph->dest));
+ pr_err("TCP:: SEQ = 0x%08X(%u), ACK = 0x%08X(%u)\n",
+ ntohs(tcph->seq), ntohs(tcph->seq),
+ ntohs(tcph->ack_seq), ntohs(tcph->ack_seq));
+ pr_err("TCP:: Flags = %s\n", tcp_flags);
+ pr_err("TCP:: Window = %u, Checksum = 0x%04X, Urg Pointer = %u\n",
+ ntohs(tcph->window), ntohs(tcph->check), ntohs(tcph->urg_ptr));
+
+ if (opt_len > 0) {
+ printk(KERN_ERR "TCP:: Options = ");
+ for (i = 0; i < opt_len; i++)
+ printk("%02X ", opt[i]);
+ printk("\n");
+ }
+}
+
+static void print_udp_header(u8 *pkt)
+{
+ struct udphdr *udph = (struct udphdr *)pkt;
+
+/*-------------------------------------------------------------------------
+
+ UDP Header Format
+
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Source Port | Destination Port |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Length | Checksum |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | data |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+-------------------------------------------------------------------------*/
+
+ pr_err("UDP:: Src Port = %u, Dst Prt = %u\n",
+ ntohs(udph->source), ntohs(udph->dest));
+ pr_err("UDP:: Length = %u, Checksum = 0x%04X\n",
+ ntohs(udph->len), ntohs(udph->check));
+
+ if (ntohs(udph->dest) == 53)
+ pr_err("UDP:: DNS query!!!\n");
+
+ if (ntohs(udph->source) == 53)
+ pr_err("UDP:: DNS response!!!\n");
+}
+
+void print_ip4_packet(u8 *ip_pkt)
+{
+ struct iphdr *iph = (struct iphdr *)ip_pkt;
+ u8 *pkt = ip_pkt + (iph->ihl << 2);
+ u16 flags = (ntohs(iph->frag_off) & 0xE000);
+ u16 frag_off = (ntohs(iph->frag_off) & 0x1FFF);
+
+ pr_err("-----------------------------------------------------------\n");
+
+/*---------------------------------------------------------------------------
+
+ IPv4 Header Format
+
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ |Version| IHL |Type of Service| Total Length |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Identification |C|D|M| Fragment Offset |
+ | |E|F|F| |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Time to Live | Protocol | Header Checksum |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Source Address |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Destination Address |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Options | Padding |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ IHL - Header Length
+ Flags - Consist of 3 bits
+ The 1st bit is "Congestion" bit.
+ The 2nd bit is "Dont Fragment" bit.
+ The 3rd bit is "More Fragments" bit.
+
+---------------------------------------------------------------------------*/
+
+ memset(ip_flags, 0, sizeof(ip_flags));
+ if (flags & IP_CE)
+ strcat(ip_flags, "CE ");
+ if (flags & IP_DF)
+ strcat(ip_flags, "DF ");
+ if (flags & IP_MF)
+ strcat(ip_flags, "MF ");
+
+ pr_err("IP4:: Version = %u, Header Length = %u, TOS = %u, Length = %u\n",
+ iph->version, (iph->ihl << 2), iph->tos, ntohs(iph->tot_len));
+ pr_err("IP4:: ID = %u, Fragment Offset = %u\n",
+ ntohs(iph->id), frag_off);
+ pr_err("IP4:: Flags = %s\n", ip_flags);
+ pr_err("IP4:: TTL = %u, Protocol = %u, Header Checksum = 0x%04X\n",
+ iph->ttl, iph->protocol, ntohs(iph->check));
+ pr_err("IP4:: Src IP Addr = %u.%u.%u.%u, Dst IP Addr = %u.%u.%u.%u\n",
+ ip_pkt[12], ip_pkt[13], ip_pkt[14], ip_pkt[15],
+ ip_pkt[16], ip_pkt[17], ip_pkt[18], ip_pkt[19]);
+
+ switch (iph->protocol) {
+ case 6:
+ /* TCP */
+ print_tcp_header(pkt);
+ break;
+
+ case 17:
+ /* UDP */
+ print_udp_header(pkt);
+ break;
+
+ default:
+ break;
+ }
+
+ pr_err("-----------------------------------------------------------\n");
+}
diff --git a/drivers/misc/modem_if_na/modem_utils.h b/drivers/misc/modem_if_na/modem_utils.h
new file mode 100644
index 0000000..0c37e1b
--- /dev/null
+++ b/drivers/misc/modem_if_na/modem_utils.h
@@ -0,0 +1,158 @@
+/*
+ * 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.
+ *
+ */
+
+#ifndef __MODEM_UTILS_H__
+#define __MODEM_UTILS_H__
+
+#define RAW_DEV(rdevs, i) (((struct io_raw_devices *)rdevs)->raw_devices[i])
+
+/**
+ * raw_devs_for_each - iterate raw devices of multi raw device
+ * @iod: struct io_device *iod
+ * @index: int index
+ * @multiraw: struct io_device *multiraw
+ */
+#define raw_devs_for_each(multiraw, index, iod) \
+ for (index = 0; iod = RAW_DEV(multiraw->private_data, index), \
+ index < MAX_RAW_DEVS; index++) \
+ if (iod)
+
+/**
+ * io_devs_for_each - iterate io devices of list_of_io_devices
+ * @iod: struct io_device *iod
+ * @ld: struct link_device *ld
+ */
+#define io_devs_for_each(iod, ld) \
+ list_for_each_entry(iod, (ld)->list_of_io_devices, list) \
+ if (iod->link_types & LINKTYPE((ld)->link_type))
+
+
+static inline struct io_device *find_iodev(struct link_device *ld,
+ enum dev_format format)
+{
+ struct io_device *iod;
+
+ io_devs_for_each(iod, ld) {
+ if (iod->format == format)
+ return iod;
+ }
+ return NULL;
+}
+
+/** countbits - count number of 1 bits as fastest way
+ * @n: number
+ */
+static inline unsigned int countbits(unsigned int n)
+{
+ unsigned int i;
+ for (i = 0; n != 0; i++)
+ n &= (n - 1);
+ return i;
+}
+
+/* print buffer as hex string */
+int pr_buffer(const char *tag, const char *data, size_t data_len,
+ size_t max_len);
+
+/* print a sk_buff as hex string */
+#define pr_skb(tag, skb) \
+ pr_buffer(tag, (char *)((skb)->data), (size_t)((skb)->len), (size_t)16)
+
+/* print a urb as hex string */
+#define pr_urb(tag, urb) \
+ pr_buffer(tag, (char *)((urb)->transfer_buffer), \
+ (size_t)((urb)->actual_length), (size_t)16)
+
+/* 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);
+
+void mif_print_data(char *buf, int len);
+void print_sipc4_hdlc_fmt_frame(const u8 *psrc);
+void print_sipc4_fmt_frame(const u8 *psrc);
+
+/*---------------------------------------------------------------------------
+
+ IPv4 Header Format
+
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ |Version| IHL |Type of Service| Total Length |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Identification |C|D|M| Fragment Offset |
+ | |E|F|F| |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Time to Live | Protocol | Header Checksum |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Source Address |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Destination Address |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Options | Padding |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ IHL - Header Length
+ Flags - Consist of 3 bits
+ The 1st bit is "Congestion" bit.
+ The 2nd bit is "Dont Fragment" bit.
+ The 3rd bit is "More Fragments" bit.
+
+---------------------------------------------------------------------------*/
+#define IPV4_HDR_SIZE 20
+
+/*-------------------------------------------------------------------------
+
+ TCP Header Format
+
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Source Port | Destination Port |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Sequence Number |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Acknowledgment Number |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Data | |C|E|U|A|P|R|S|F| |
+ | Offset| Rsvd |W|C|R|C|S|S|Y|I| Window |
+ | | |R|E|G|K|H|T|N|N| |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Checksum | Urgent Pointer |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Options | Padding |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | data |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+-------------------------------------------------------------------------*/
+#define TCP_HDR_SIZE 20
+
+/*-------------------------------------------------------------------------
+
+ UDP Header Format
+
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Source Port | Destination Port |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Length | Checksum |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | data |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+-------------------------------------------------------------------------*/
+#define UDP_HDR_SIZE 8
+
+void print_ip4_packet(u8 *ip_pkt);
+
+#endif/*__MODEM_UTILS_H__*/
diff --git a/drivers/misc/modem_if_na/modem_variation.h b/drivers/misc/modem_if_na/modem_variation.h
new file mode 100644
index 0000000..1bf2d13
--- /dev/null
+++ b/drivers/misc/modem_if_na/modem_variation.h
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2010 Google, Inc.
+ * Copyright (C) 2010 Samsung Electronics.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __MODEM_VARIATION_H__
+#define __MODEM_VARIATION_H__
+
+#define DECLARE_MODEM_INIT(type) \
+ int type ## _init_modemctl_device(struct modem_ctl *mc, \
+ struct modem_data *pdata)
+#define DECLARE_MODEM_INIT_DUMMY(type) \
+ DECLARE_MODEM_INIT(type) { return 0; }
+
+#define DECLARE_LINK_INIT(type) \
+ struct link_device *type ## _create_link_device( \
+ struct platform_device *pdev)
+#define DECLARE_LINK_INIT_DUMMY(type) \
+ DECLARE_LINK_INIT(type) { return NULL; }
+
+#define MODEM_INIT_CALL(type) type ## _init_modemctl_device
+#define LINK_INIT_CALL(type) type ## _create_link_device
+
+/* add declaration of modem & link type */
+/* modem device support */
+#ifdef CONFIG_UMTS_MODEM_XMM6260
+DECLARE_MODEM_INIT(xmm6260);
+#else
+DECLARE_MODEM_INIT_DUMMY(xmm6260)
+#endif
+
+#ifdef CONFIG_LTE_MODEM_CMC221
+DECLARE_MODEM_INIT(cmc221);
+#else
+DECLARE_MODEM_INIT_DUMMY(cmc221)
+#endif
+
+#ifdef CONFIG_CDMA_MODEM_CBP71
+DECLARE_MODEM_INIT(cbp71);
+#else
+DECLARE_MODEM_INIT_DUMMY(cbp71)
+#endif
+
+#ifdef CONFIG_LTE_MODEM_CMC220
+DECLARE_MODEM_INIT(cmc220);
+#else
+DECLARE_MODEM_INIT_DUMMY(cmc220)
+#endif
+/* link device support */
+#ifdef CONFIG_UMTS_LINK_MIPI
+DECLARE_LINK_INIT(mipi);
+#else
+DECLARE_LINK_INIT_DUMMY(mipi)
+#endif
+
+#ifdef CONFIG_LINK_DEVICE_DPRAM
+DECLARE_LINK_INIT(dpram);
+#else
+DECLARE_LINK_INIT_DUMMY(dpram)
+#endif
+
+#ifdef CONFIG_LINK_DEVICE_USB
+DECLARE_LINK_INIT(usb);
+#else
+DECLARE_LINK_INIT_DUMMY(usb)
+#endif
+
+#ifdef CONFIG_UMTS_LINK_HSIC
+DECLARE_LINK_INIT(hsic);
+#else
+DECLARE_LINK_INIT_DUMMY(hsic)
+#endif
+
+#ifdef CONFIG_UMTS_LINK_SPI
+DECLARE_LINK_INIT(spi);
+#else
+DECLARE_LINK_INIT_DUMMY(spi)
+#endif
+
+typedef int (*modem_init_call)(struct modem_ctl *, struct modem_data *);
+modem_init_call modem_init_func[] = {
+ MODEM_INIT_CALL(xmm6260),
+ MODEM_INIT_CALL(cbp71),
+ MODEM_INIT_CALL(cmc221),
+ MODEM_INIT_CALL(cmc220),
+};
+
+typedef struct link_device *(*link_init_call)(struct platform_device *);
+link_init_call link_init_func[] = {
+ LINK_INIT_CALL(mipi),
+ LINK_INIT_CALL(dpram),
+ LINK_INIT_CALL(spi),
+ LINK_INIT_CALL(usb),
+ LINK_INIT_CALL(hsic),
+};
+
+static int call_modem_init_func(struct modem_ctl *mc, struct modem_data *pdata)
+{
+ if (modem_init_func[pdata->modem_type])
+ return modem_init_func[pdata->modem_type](mc, pdata);
+ else
+ return -ENOTSUPP;
+}
+
+static struct link_device *call_link_init_func(struct platform_device *pdev,
+ enum modem_link link_type)
+{
+ if (link_init_func[link_type])
+ return link_init_func[link_type](pdev);
+ else
+ return NULL;
+}
+
+#endif
diff --git a/drivers/misc/mpu3050/accel/kxtf9.c b/drivers/misc/mpu3050/accel/kxtf9.c
index f438259..8069f3c 100755..100644
--- a/drivers/misc/mpu3050/accel/kxtf9.c
+++ b/drivers/misc/mpu3050/accel/kxtf9.c
@@ -1,20 +1,19 @@
/*
- $License:
- Copyright (C) 2010 InvenSense Corporation, All Rights Reserved.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- 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.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
- $
+ * $License:
+ * Copyright (C) 2010 InvenSense Corporation, All Rights Reserved.
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * 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.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
@@ -46,45 +45,45 @@
#undef MPL_LOG_TAG
#define MPL_LOG_TAG "MPL-acc"
-#define KXTF9_XOUT_HPF_L (0x00) /* 0000 0000 */
-#define KXTF9_XOUT_HPF_H (0x01) /* 0000 0001 */
-#define KXTF9_YOUT_HPF_L (0x02) /* 0000 0010 */
-#define KXTF9_YOUT_HPF_H (0x03) /* 0000 0011 */
-#define KXTF9_ZOUT_HPF_L (0x04) /* 0001 0100 */
-#define KXTF9_ZOUT_HPF_H (0x05) /* 0001 0101 */
-#define KXTF9_XOUT_L (0x06) /* 0000 0110 */
-#define KXTF9_XOUT_H (0x07) /* 0000 0111 */
-#define KXTF9_YOUT_L (0x08) /* 0000 1000 */
-#define KXTF9_YOUT_H (0x09) /* 0000 1001 */
-#define KXTF9_ZOUT_L (0x0A) /* 0001 1010 */
-#define KXTF9_ZOUT_H (0x0B) /* 0001 1011 */
-#define KXTF9_ST_RESP (0x0C) /* 0000 1100 */
-#define KXTF9_WHO_AM_I (0x0F) /* 0000 1111 */
-#define KXTF9_TILT_POS_CUR (0x10) /* 0001 0000 */
-#define KXTF9_TILT_POS_PRE (0x11) /* 0001 0001 */
-#define KXTF9_INT_SRC_REG1 (0x15) /* 0001 0101 */
-#define KXTF9_INT_SRC_REG2 (0x16) /* 0001 0110 */
-#define KXTF9_STATUS_REG (0x18) /* 0001 1000 */
-#define KXTF9_INT_REL (0x1A) /* 0001 1010 */
-#define KXTF9_CTRL_REG1 (0x1B) /* 0001 1011 */
-#define KXTF9_CTRL_REG2 (0x1C) /* 0001 1100 */
-#define KXTF9_CTRL_REG3 (0x1D) /* 0001 1101 */
-#define KXTF9_INT_CTRL_REG1 (0x1E) /* 0001 1110 */
-#define KXTF9_INT_CTRL_REG2 (0x1F) /* 0001 1111 */
-#define KXTF9_INT_CTRL_REG3 (0x20) /* 0010 0000 */
-#define KXTF9_DATA_CTRL_REG (0x21) /* 0010 0001 */
-#define KXTF9_TILT_TIMER (0x28) /* 0010 1000 */
-#define KXTF9_WUF_TIMER (0x29) /* 0010 1001 */
-#define KXTF9_TDT_TIMER (0x2B) /* 0010 1011 */
-#define KXTF9_TDT_H_THRESH (0x2C) /* 0010 1100 */
-#define KXTF9_TDT_L_THRESH (0x2D) /* 0010 1101 */
-#define KXTF9_TDT_TAP_TIMER (0x2E) /* 0010 1110 */
-#define KXTF9_TDT_TOTAL_TIMER (0x2F) /* 0010 1111 */
-#define KXTF9_TDT_LATENCY_TIMER (0x30) /* 0011 0000 */
-#define KXTF9_TDT_WINDOW_TIMER (0x31) /* 0011 0001 */
-#define KXTF9_WUF_THRESH (0x5A) /* 0101 1010 */
-#define KXTF9_TILT_ANGLE (0x5C) /* 0101 1100 */
-#define KXTF9_HYST_SET (0x5F) /* 0101 1111 */
+#define KXTF9_XOUT_HPF_L (0x00) /* 0000 0000 */
+#define KXTF9_XOUT_HPF_H (0x01) /* 0000 0001 */
+#define KXTF9_YOUT_HPF_L (0x02) /* 0000 0010 */
+#define KXTF9_YOUT_HPF_H (0x03) /* 0000 0011 */
+#define KXTF9_ZOUT_HPF_L (0x04) /* 0001 0100 */
+#define KXTF9_ZOUT_HPF_H (0x05) /* 0001 0101 */
+#define KXTF9_XOUT_L (0x06) /* 0000 0110 */
+#define KXTF9_XOUT_H (0x07) /* 0000 0111 */
+#define KXTF9_YOUT_L (0x08) /* 0000 1000 */
+#define KXTF9_YOUT_H (0x09) /* 0000 1001 */
+#define KXTF9_ZOUT_L (0x0A) /* 0001 1010 */
+#define KXTF9_ZOUT_H (0x0B) /* 0001 1011 */
+#define KXTF9_ST_RESP (0x0C) /* 0000 1100 */
+#define KXTF9_WHO_AM_I (0x0F) /* 0000 1111 */
+#define KXTF9_TILT_POS_CUR (0x10) /* 0001 0000 */
+#define KXTF9_TILT_POS_PRE (0x11) /* 0001 0001 */
+#define KXTF9_INT_SRC_REG1 (0x15) /* 0001 0101 */
+#define KXTF9_INT_SRC_REG2 (0x16) /* 0001 0110 */
+#define KXTF9_STATUS_REG (0x18) /* 0001 1000 */
+#define KXTF9_INT_REL (0x1A) /* 0001 1010 */
+#define KXTF9_CTRL_REG1 (0x1B) /* 0001 1011 */
+#define KXTF9_CTRL_REG2 (0x1C) /* 0001 1100 */
+#define KXTF9_CTRL_REG3 (0x1D) /* 0001 1101 */
+#define KXTF9_INT_CTRL_REG1 (0x1E) /* 0001 1110 */
+#define KXTF9_INT_CTRL_REG2 (0x1F) /* 0001 1111 */
+#define KXTF9_INT_CTRL_REG3 (0x20) /* 0010 0000 */
+#define KXTF9_DATA_CTRL_REG (0x21) /* 0010 0001 */
+#define KXTF9_TILT_TIMER (0x28) /* 0010 1000 */
+#define KXTF9_WUF_TIMER (0x29) /* 0010 1001 */
+#define KXTF9_TDT_TIMER (0x2B) /* 0010 1011 */
+#define KXTF9_TDT_H_THRESH (0x2C) /* 0010 1100 */
+#define KXTF9_TDT_L_THRESH (0x2D) /* 0010 1101 */
+#define KXTF9_TDT_TAP_TIMER (0x2E) /* 0010 1110 */
+#define KXTF9_TDT_TOTAL_TIMER (0x2F) /* 0010 1111 */
+#define KXTF9_TDT_LATENCY_TIMER (0x30) /* 0011 0000 */
+#define KXTF9_TDT_WINDOW_TIMER (0x31) /* 0011 0001 */
+#define KXTF9_WUF_THRESH (0x5A) /* 0101 1010 */
+#define KXTF9_TILT_ANGLE (0x5C) /* 0101 1100 */
+#define KXTF9_HYST_SET (0x5F) /* 0101 1111 */
#define KXTF9_MAX_DUR (0xFF)
#define KXTF9_MAX_THS (0xFF)
@@ -114,9 +113,9 @@ struct kxtf9_private_data {
};
extern struct acc_data cal_data;
-/*****************************************
- Accelerometer Initialization Functions
-*****************************************/
+/*
+ * Accelerometer Initialization Functions
+ */
static int kxtf9_set_ths(void *mlsl_handle,
struct ext_slave_platform_data *pdata,
@@ -364,6 +363,8 @@ static int kxtf9_resume(void *mlsl_handle,
KXTF9_CTRL_REG1,
private_data->resume.ctrl_reg1);
ERROR_CHECK(result);
+ MLOSSleep(20);
+
result = MLSLSerialRead(mlsl_handle, pdata->address,
KXTF9_INT_REL, 1, &data);
ERROR_CHECK(result);
@@ -377,28 +378,51 @@ static int kxtf9_init(void *mlsl_handle,
{
struct kxtf9_private_data *private_data;
+ unsigned char i, buf;
+ unsigned char tf9_addr[] = { 0x0F, 0x0E, 0x0D, 0x0C };
+ unsigned char tmp_addr = 0;
+
int result = ML_SUCCESS;
private_data = (struct kxtf9_private_data *)
- MLOSMalloc(sizeof(struct kxtf9_private_data));
+ MLOSMalloc(sizeof(struct kxtf9_private_data));
if (!private_data)
return ML_ERROR_MEMORY_EXAUSTED;
+ for (i = 0; i < 4; i++) {
+ pr_info("%s: #%d: try to access with 0x%02x slave address",
+ __func__, i, tf9_addr[i]);
+
+ /* Check Device ID */
+ result = MLSLSerialRead(mlsl_handle,
+ tf9_addr[i], KXTF9_WHO_AM_I, 1, &buf);
+
+ pr_info("%s : WHO_AM_I = 0x%02x", __func__, buf);
+ if (result == ML_SUCCESS) {
+ tmp_addr = tf9_addr[i];
+ pr_info("%s : slave addr = 0x%02x", __func__, tmp_addr);
+ break;
+ }
+ }
+
/* RAM reset */
- result = MLSLSerialWriteSingle(mlsl_handle,
- pdata->address, KXTF9_CTRL_REG1, 0x40);
- /* Fastest Reset */
+ result = MLSLSerialWriteSingle(mlsl_handle, tmp_addr,
+ KXTF9_CTRL_REG3, 0xcd);
ERROR_CHECK(result);
- result = MLSLSerialWriteSingle(mlsl_handle,
- pdata->address, KXTF9_DATA_CTRL_REG, 0x36);
- /* Fastest Reset */
+ MLOSSleep(100);
+
+ /* Set ODR(Output Data Rate) */
+ result = MLSLSerialWriteSingle(mlsl_handle, pdata->address,
+ KXTF9_DATA_CTRL_REG, 0x36);
ERROR_CHECK(result);
- result = MLSLSerialWriteSingle(mlsl_handle,
- pdata->address, KXTF9_CTRL_REG3, 0xcd);
- /* Reset */
+
+ /* Set Main feature as 2g(Bit4/3=00)
+ 12-bit(Bit-6=1) and turn on standby mode(Bit-7=0)
+ */
+ result = MLSLSerialWriteSingle(mlsl_handle, pdata->address,
+ KXTF9_CTRL_REG1, 0x40);
ERROR_CHECK(result);
- MLOSSleep(2);
pdata->private_data = private_data;
@@ -594,20 +618,20 @@ static int kxtf9_read(void *mlsl_handle,
}
static struct ext_slave_descr kxtf9_descr = {
- /*.init = */ kxtf9_init,
- /*.exit = */ kxtf9_exit,
- /*.suspend = */ kxtf9_suspend,
- /*.resume = */ kxtf9_resume,
- /*.read = */ kxtf9_read,
- /*.config = */ kxtf9_config,
- /*.get_config = */ kxtf9_get_config,
- /*.name = */ "kxtf9",
- /*.type = */ EXT_SLAVE_TYPE_ACCELEROMETER,
- /*.id = */ ACCEL_ID_KXTF9,
- /*.reg = */ 0x06,
- /*.len = */ 6,
- /*.endian = */ EXT_SLAVE_LITTLE_ENDIAN,
- /*.range = */ {2, 0},
+ /*.init = */ kxtf9_init,
+ /*.exit = */ kxtf9_exit,
+ /*.suspend = */ kxtf9_suspend,
+ /*.resume = */ kxtf9_resume,
+ /*.read = */ kxtf9_read,
+ /*.config = */ kxtf9_config,
+ /*.get_config = */ kxtf9_get_config,
+ /*.name = */ "kxtf9",
+ /*.type = */ EXT_SLAVE_TYPE_ACCELEROMETER,
+ /*.id = */ ACCEL_ID_KXTF9,
+ /*.reg = */ 0x06,
+ /*.len = */ 6,
+ /*.endian = */ EXT_SLAVE_LITTLE_ENDIAN,
+ /*.range = */ {2, 0},
};
struct ext_slave_descr *kxtf9_get_slave_descr(void)
diff --git a/drivers/misc/sec_jack.c b/drivers/misc/sec_jack.c
index d2d4524..df77d33 100644
--- a/drivers/misc/sec_jack.c
+++ b/drivers/misc/sec_jack.c
@@ -55,16 +55,20 @@
#define WAKE_LOCK_TIME (HZ * 5) /* 5 sec */
#define EAR_CHECK_LOOP_CNT 10
-#if defined(CONFIG_MACH_PX) || defined(CONFIG_MACH_P4NOTE)
+#if defined(CONFIG_MACH_PX) || defined(CONFIG_MACH_P4NOTE) \
+ || defined(CONFIG_MACH_GC1)
#define JACK_CLASS_NAME "audio"
#define JACK_DEV_NAME "earjack"
#else
#define JACK_CLASS_NAME "jack"
#define JACK_DEV_NAME "jack_selector"
#endif
+#define JACK_RESELECTOR_NAME "jack_reselector"
static struct class *jack_class;
static struct device *jack_dev;
+static struct device *jack_reselector;
+static bool recheck_jack;
struct sec_jack_info {
struct s3c_adc_client *padc;
@@ -338,8 +342,16 @@ static void determine_jack_type(struct sec_jack_info *hi)
for (i = 0; i < size; i++) {
if (adc <= zones[i].adc_high) {
if (++count[i] > zones[i].check_count) {
+ if (recheck_jack == true && i == 4) {
+ pr_info("%s : something wrong connection!\n",
+ __func__);
+ handle_jack_not_inserted(hi);
+
+ recheck_jack = false;
+ return;
+ }
sec_jack_set_type(hi,
- zones[i].jack_type);
+ zones[i].jack_type);
return;
}
msleep(zones[i].delay_ms);
@@ -348,6 +360,7 @@ static void determine_jack_type(struct sec_jack_info *hi)
}
}
+ recheck_jack = false;
/* jack removed before detection complete */
pr_debug("%s : jack removed before detection complete\n", __func__);
handle_jack_not_inserted(hi);
@@ -474,7 +487,8 @@ static ssize_t select_jack_store(struct device *dev,
return size;
}
-#if defined(CONFIG_MACH_PX) || defined(CONFIG_MACH_P4NOTE)
+#if defined(CONFIG_MACH_PX) || defined(CONFIG_MACH_P4NOTE) \
+ || defined(CONFIG_MACH_GC1)
static ssize_t earjack_key_state_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -528,6 +542,36 @@ static DEVICE_ATTR(state, S_IRUGO | S_IWUSR | S_IWGRP,
static DEVICE_ATTR(select_jack, S_IRUGO | S_IWUSR | S_IWGRP,
select_jack_show, select_jack_store);
+static ssize_t reselect_jack_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ pr_info("%s : operate nothing\n", __func__);
+
+ return 0;
+}
+
+static ssize_t reselect_jack_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct sec_jack_info *hi = dev_get_drvdata(dev);
+ struct sec_jack_platform_data *pdata = hi->pdata;
+ int value = 0;
+
+
+ sscanf(buf, "%d", &value);
+ pr_err("%s: User reselection : 0X%x", __func__, value);
+
+ if (value == 1) {
+ recheck_jack = true;
+ determine_jack_type(hi);
+ }
+
+ return size;
+}
+
+static DEVICE_ATTR(reselect_jack, S_IRUGO | S_IWUSR | S_IWGRP,
+ reselect_jack_show, reselect_jack_store);
+
static int sec_jack_probe(struct platform_device *pdev)
{
struct sec_jack_info *hi;
@@ -618,7 +662,19 @@ static int sec_jack_probe(struct platform_device *pdev)
if (device_create_file(jack_dev, &dev_attr_select_jack) < 0)
pr_err("Failed to create device file(%s)!\n",
dev_attr_select_jack.attr.name);
-#if defined(CONFIG_MACH_PX) || defined(CONFIG_MACH_P4NOTE)
+
+ jack_reselector = device_create(jack_class, NULL, 0, hi,
+ JACK_RESELECTOR_NAME);
+ if (IS_ERR(jack_reselector))
+ pr_err("Failed to create device(sec_jack)!= %ld\n",
+ IS_ERR(jack_reselector));
+
+ if (device_create_file(jack_reselector, &dev_attr_reselect_jack) < 0)
+ pr_err("Failed to create device file(%s)!\n",
+ dev_attr_reselect_jack.attr.name);
+
+#if defined(CONFIG_MACH_PX) || defined(CONFIG_MACH_P4NOTE) \
+ || defined(CONFIG_MACH_GC1)
if (device_create_file(jack_dev, &dev_attr_key_state) < 0)
pr_err("Failed to create device file (%s)!\n",
dev_attr_key_state.attr.name);
diff --git a/drivers/misc/sec_jack_muic.c b/drivers/misc/sec_jack_muic.c
new file mode 100644
index 0000000..9450f1c
--- /dev/null
+++ b/drivers/misc/sec_jack_muic.c
@@ -0,0 +1,461 @@
+/* drivers/misc/sec_jack_muic.c
+ *
+ * Copyright (C) 2010 Samsung Electronics Co.Ltd
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/input.h>
+#include <linux/platform_device.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/switch.h>
+#include <linux/input.h>
+#include <linux/timer.h>
+#include <linux/wakelock.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/gpio_event.h>
+#include <linux/sec_jack.h>
+
+#include <plat/adc.h>
+
+/* keep this value if you support double-pressed concept */
+#if defined(CONFIG_TARGET_LOCALE_KOR)
+#define SEND_KEY_CHECK_TIME_MS 20 /* 20ms - GB VOC in KOR*/
+#elif defined(CONFIG_MACH_Q1_BD)
+/* 27ms, total delay is approximately double more
+ because hrtimer is called twice by gpio input driver,
+ new sec spec total delay is 60ms +/-10ms */
+#define SEND_KEY_CHECK_TIME_MS 27
+#else
+#define SEND_KEY_CHECK_TIME_MS 40 /* 40ms */
+#endif
+#define WAKE_LOCK_TIME (HZ * 5) /* 5 sec */
+#define WAKE_LOCK_TIME_IN_SENDKEY (HZ * 3)
+
+#define JACK_CLASS_NAME "audio"
+#define JACK_DEV_NAME "earjack"
+
+static struct class *jack_class;
+static struct device *jack_dev;
+
+struct sec_jack_info {
+ struct s3c_adc_client *padc;
+ struct sec_jack_platform_data *pdata;
+ struct delayed_work jack_detect_work;
+ struct work_struct buttons_work;
+ struct workqueue_struct *queue;
+ struct input_dev *input_dev;
+ struct wake_lock det_wake_lock;
+ struct sec_jack_zone *zone;
+ struct input_handler handler;
+ struct input_handle handle;
+ struct input_device_id ids[2];
+ int det_irq;
+ int dev_id;
+ int pressed;
+ int pressed_code;
+ struct platform_device *send_key_dev;
+ unsigned int cur_jack_type;
+ int det_status;
+};
+
+struct sec_jack_info *hi;
+static unsigned int send_end_pressed;
+
+/* with some modifications like moving all the gpio structs inside
+ * the platform data and getting the name for the switch and
+ * gpio_event from the platform data, the driver could support more than
+ * one headset jack, but currently user space is looking only for
+ * one key file and switch for a headset so it'd be overkill and
+ * untestable so we limit to one instantiation for now.
+ */
+static atomic_t instantiated = ATOMIC_INIT(0);
+
+/* sysfs name HeadsetObserver.java looks for to track headset state
+ */
+struct switch_dev switch_jack_detection = {
+ .name = "h2w",
+};
+
+/* To support AT+FCESTEST=1 */
+struct switch_dev switch_sendend = {
+ .name = "send_end",
+};
+
+static struct gpio_event_direct_entry sec_jack_key_map[] = {
+ {
+ .code = KEY_UNKNOWN,
+ },
+};
+
+static struct gpio_event_input_info sec_jack_key_info = {
+ .info.func = gpio_event_input_func,
+ .info.no_suspend = true,
+ .type = EV_KEY,
+ .debounce_time.tv64 = SEND_KEY_CHECK_TIME_MS * NSEC_PER_MSEC,
+ .keymap = sec_jack_key_map,
+ .keymap_size = ARRAY_SIZE(sec_jack_key_map)
+};
+
+static struct gpio_event_info *sec_jack_input_info[] = {
+ &sec_jack_key_info.info,
+};
+
+static struct gpio_event_platform_data sec_jack_input_data = {
+ .name = "sec_jack",
+ .info = sec_jack_input_info,
+ .info_count = ARRAY_SIZE(sec_jack_input_info),
+};
+
+static void sec_jack_set_type(struct sec_jack_info *hi, int jack_type)
+{
+ struct sec_jack_platform_data *pdata = hi->pdata;
+
+ /* this can happen during slow inserts where we think we identified
+ * the type but then we get another interrupt and do it again
+ */
+ if (jack_type == hi->cur_jack_type) {
+ if (jack_type != SEC_HEADSET_4POLE)
+ pdata->set_micbias_state(false);
+
+ return;
+ }
+
+ if (jack_type == SEC_HEADSET_4POLE) {
+ /* for a 4 pole headset, enable detection of send/end key */
+ if (hi->send_key_dev == NULL)
+ /* enable to get events again */
+ hi->send_key_dev = platform_device_register_data(NULL,
+ GPIO_EVENT_DEV_NAME,
+ hi->dev_id,
+ &sec_jack_input_data,
+ sizeof(sec_jack_input_data));
+ } else {
+ /* for all other jacks, disable send/end key detection */
+ if (hi->send_key_dev != NULL) {
+ /* disable to prevent false events on next insert */
+ platform_device_unregister(hi->send_key_dev);
+ hi->send_key_dev = NULL;
+ }
+ /* micbias is left enabled for 4pole and disabled otherwise */
+ pdata->set_micbias_state(false);
+ }
+ /* if user inserted ear jack slowly, different jack event can occur
+ * sometimes because irq_thread is defined IRQ_ONESHOT, detach status
+ * can be ignored sometimes so in that case, driver inform detach
+ * event to user side
+ */
+ switch_set_state(&switch_jack_detection, SEC_JACK_NO_DEVICE);
+
+ hi->cur_jack_type = jack_type;
+ pr_info("%s : jack_type = %d\n", __func__, jack_type);
+
+ switch_set_state(&switch_jack_detection, jack_type);
+}
+
+static void handle_jack_not_inserted(struct sec_jack_info *hi)
+{
+ sec_jack_set_type(hi, SEC_JACK_NO_DEVICE);
+ hi->pdata->set_micbias_state(false);
+}
+
+static ssize_t select_jack_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ pr_info("%s : operate nothing\n", __func__);
+
+ return 0;
+}
+
+static ssize_t select_jack_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct sec_jack_info *hi = dev_get_drvdata(dev);
+ struct sec_jack_platform_data *pdata = hi->pdata;
+ int value = 0;
+
+
+ sscanf(buf, "%d", &value);
+ pr_err("%s: User selection : 0X%x", __func__, value);
+ if (value == SEC_HEADSET_4POLE) {
+ pdata->set_micbias_state(true);
+ msleep(100);
+ }
+
+ sec_jack_set_type(hi, value);
+
+ return size;
+}
+
+static ssize_t earjack_key_state_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct sec_jack_info *hi = dev_get_drvdata(dev);
+ int value = 0;
+
+ if (hi->pressed <= 0)
+ value = 0;
+ else
+ value = 1;
+
+ return sprintf(buf, "%d\n", value);
+}
+
+static ssize_t earjack_key_state_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ pr_info("%s : operate nothing\n", __func__);
+
+ return size;
+}
+
+static ssize_t earjack_state_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct sec_jack_info *hi = dev_get_drvdata(dev);
+ int value = 0;
+
+ if (hi->cur_jack_type == SEC_HEADSET_4POLE)
+ value = 1;
+ else
+ value = 0;
+
+ return sprintf(buf, "%d\n", value);
+}
+
+static ssize_t earjack_state_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ pr_info("%s : operate nothing\n", __func__);
+
+ return size;
+}
+static DEVICE_ATTR(key_state, S_IRUGO | S_IWUSR | S_IWGRP,
+ earjack_key_state_show, earjack_key_state_store);
+
+static DEVICE_ATTR(state, S_IRUGO | S_IWUSR | S_IWGRP,
+ earjack_state_show, earjack_state_store);
+
+static DEVICE_ATTR(select_jack, S_IRUGO | S_IWUSR | S_IWGRP,
+ select_jack_show, select_jack_store);
+
+void jack_status_change(int status)
+{
+ int jack_type = SEC_JACK_NO_DEVICE;
+
+ pr_info("%s status=%d\n", __func__, status);
+
+ wake_lock_timeout(&hi->det_wake_lock, WAKE_LOCK_TIME);
+
+ if (status) {
+ jack_type = SEC_HEADSET_4POLE;
+ } else {
+ jack_type = SEC_JACK_NO_DEVICE;
+ if (send_end_pressed == 1) {
+ input_report_key(hi->input_dev, KEY_MEDIA, 0);
+ input_sync(hi->input_dev);
+ switch_set_state(&switch_sendend, 0);
+ send_end_pressed = 0;
+ }
+ }
+ switch_set_state(&switch_jack_detection, jack_type);
+ hi->cur_jack_type = jack_type;
+}
+EXPORT_SYMBOL(jack_status_change);
+
+void earkey_status_change(int pressed, int kcode)
+{
+ wake_lock_timeout(&hi->det_wake_lock, WAKE_LOCK_TIME_IN_SENDKEY);
+ hi->pressed_code = kcode;
+ if (pressed) {
+ input_report_key(hi->input_dev, hi->pressed_code, 1);
+ switch_set_state(&switch_sendend, 1);
+ input_sync(hi->input_dev);
+ send_end_pressed = 1;
+ } else {
+ input_report_key(hi->input_dev, hi->pressed_code, 0);
+ switch_set_state(&switch_sendend, 0);
+ input_sync(hi->input_dev);
+ send_end_pressed = 0;
+ }
+ hi->pressed = send_end_pressed;
+}
+EXPORT_SYMBOL(earkey_status_change);
+
+static int sec_jack_probe(struct platform_device *pdev)
+{
+ struct sec_jack_platform_data *pdata = pdev->dev.platform_data;
+ int ret;
+
+ pr_info("%s : Registering jack driver\n", __func__);
+ if (!pdata) {
+ pr_err("%s : pdata is NULL.\n", __func__);
+ return -ENODEV;
+ }
+
+ if (atomic_xchg(&instantiated, 1)) {
+ pr_err("%s : already instantiated, can only have one\n",
+ __func__);
+ return -ENODEV;
+ }
+
+ hi = kzalloc(sizeof(struct sec_jack_info), GFP_KERNEL);
+ if (hi == NULL) {
+ pr_err("%s : Failed to allocate memory.\n", __func__);
+ ret = -ENOMEM;
+ goto err_kzalloc;
+ }
+
+ hi->pdata = pdata;
+ send_end_pressed = 0;
+
+ /* make the id of our gpio_event device the same as our platform device,
+ * which makes it the responsiblity of the board file to make sure
+ * it is unique relative to other gpio_event devices
+ */
+ hi->dev_id = pdev->id;
+
+ hi->input_dev = input_allocate_device();
+ if (hi->input_dev == NULL) {
+ ret = -ENOMEM;
+ pr_err("%s : Failed to allocate input device.\n", __func__);
+ }
+ pr_info("%s input device is [%s]\n", __func__, hi->input_dev->name);
+
+ hi->input_dev->name = "sec_jack";
+ input_set_capability(hi->input_dev , EV_KEY, KEY_MEDIA);
+ input_set_capability(hi->input_dev , EV_KEY, KEY_VOLUMEDOWN);
+ input_set_capability(hi->input_dev , EV_KEY, KEY_VOLUMEUP);
+ ret = input_register_device(hi->input_dev);
+ if (ret) {
+ pr_err("%s : Failed to register driver\n", __func__);
+ goto err_register_input_dev;
+ }
+
+ ret = switch_dev_register(&switch_jack_detection);
+ if (ret < 0) {
+ pr_err("%s : Failed to register switch device\n", __func__);
+ goto err_register_input_dev;
+ }
+
+ ret = switch_dev_register(&switch_sendend);
+ if (ret < 0) {
+ printk(KERN_ERR "SEC JACK: Failed to register switch device\n");
+ goto err_switch_dev_register_send_end;
+ }
+ wake_lock_init(&hi->det_wake_lock, WAKE_LOCK_SUSPEND, "sec_jack_det");
+
+ hi->queue = create_singlethread_workqueue("sec_jack_wq");
+ if (hi->queue == NULL) {
+ ret = -ENOMEM;
+ pr_err("%s: Failed to create workqueue\n", __func__);
+ goto err_create_wq_failed;
+ }
+
+ jack_class = class_create(THIS_MODULE, JACK_CLASS_NAME);
+ if (IS_ERR(jack_class))
+ pr_err("Failed to create class(sec_jack)\n");
+
+ /* support PBA function test */
+ jack_dev = device_create(jack_class, NULL, 0, hi, JACK_DEV_NAME);
+ if (IS_ERR(jack_dev))
+ pr_err("Failed to create device(sec_jack)!= %ld\n",
+ IS_ERR(jack_dev));
+
+ if (device_create_file(jack_dev, &dev_attr_select_jack) < 0)
+ pr_err("Failed to create device file(%s)!\n",
+ dev_attr_select_jack.attr.name);
+
+ if (device_create_file(jack_dev, &dev_attr_key_state) < 0)
+ pr_err("Failed to create device file (%s)!\n",
+ dev_attr_key_state.attr.name);
+
+ if (device_create_file(jack_dev, &dev_attr_state) < 0)
+ pr_err("Failed to create device file (%s)!\n",
+ dev_attr_state.attr.name);
+
+ return 0;
+
+err_create_wq_failed:
+ destroy_workqueue(hi->queue);
+ wake_lock_destroy(&hi->det_wake_lock);
+ switch_dev_unregister(&switch_sendend);
+err_switch_dev_register_send_end:
+ switch_dev_unregister(&switch_jack_detection);
+err_register_input_dev:
+ input_free_device(hi->input_dev);
+err_kzalloc:
+ atomic_set(&instantiated, 0);
+
+ return ret;
+}
+
+static int sec_jack_remove(struct platform_device *pdev)
+{
+
+ struct sec_jack_info *hi = dev_get_drvdata(&pdev->dev);
+
+ pr_info("%s :\n", __func__);
+ destroy_workqueue(hi->queue);
+ if (hi->send_key_dev) {
+ platform_device_unregister(hi->send_key_dev);
+ hi->send_key_dev = NULL;
+ }
+ input_unregister_handler(&hi->handler);
+ wake_lock_destroy(&hi->det_wake_lock);
+ switch_dev_unregister(&switch_sendend);
+ switch_dev_unregister(&switch_jack_detection);
+ kfree(hi);
+ atomic_set(&instantiated, 0);
+
+ return 0;
+}
+
+
+static struct platform_driver sec_jack_driver = {
+ .probe = sec_jack_probe,
+ .remove = sec_jack_remove,
+ .driver = {
+ .name = "sec_jack",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init sec_jack_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&sec_jack_driver);
+
+ if (ret)
+ pr_err("%s: Failed to add sec jack driver\n", __func__);
+
+ return ret;
+}
+
+static void __exit sec_jack_exit(void)
+{
+ platform_driver_unregister(&sec_jack_driver);
+}
+
+module_init(sec_jack_init);
+module_exit(sec_jack_exit);
+
+MODULE_AUTHOR("sopia.kim@samsung.com");
+MODULE_DESCRIPTION("Samsung Electronics Corp Ear-Jack detection driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/misc/tzic.c b/drivers/misc/tzic.c
index 93e25bd..e1da409 100644
--- a/drivers/misc/tzic.c
+++ b/drivers/misc/tzic.c
@@ -50,15 +50,60 @@ u32 exynos_smc1(u32 cmd, u32 arg1, u32 arg2, u32 arg3)
return reg0;
}
+#if defined(CONFIG_FELICA)
+int exynos_smc_read_oemflag(u32 ctrl_word, u32 *val)
+{
+ register u32 reg0 __asm__("r0");
+ register u32 reg1 __asm__("r1");
+ register u32 reg2 __asm__("r2");
+ register u32 reg3 __asm__("r3");
+ u32 idx = 0;
+
+ for (idx = 0; reg2 != ctrl_word; idx++) {
+ reg0 = -202;
+ reg1 = 1;
+ reg2 = idx;
+
+ __asm__ volatile (
+ ".arch_extension sec\n"
+ "smc 0\n"
+ :"+r" (reg0), "+r"(reg1),
+ "+r"(reg2), "+r"(reg3)
+ );
+ if (reg1)
+ return -1;
+ }
+
+ reg0 = -202;
+ reg1 = 1;
+ reg2 = idx;
+
+ __asm__ volatile (
+ ".arch_extension sec\n"
+ "smc 0\n"
+ :"+r" (reg0), "+r"(reg1), "+r"(reg2),
+ "+r"(reg3)
+ );
+ if (reg1)
+ return -1;
+
+ *val = reg2;
+
+ return 0;
+}
+#endif
+
static DEFINE_MUTEX(tzic_mutex);
static struct class *driver_class;
static dev_t tzic_device_no;
static struct cdev tzic_cdev;
#define LOG printk
+#define TZIC_IOC_MAGIC 0x9E
+#define TZIC_IOCTL_SET_FUSE_REQ _IO(TZIC_IOC_MAGIC, 1)
+#define TZIC_IOCTL_GET_FUSE_REQ _IOR(TZIC_IOC_MAGIC, 0, unsigned int)
-static long tzic_ioctl(struct file *file, unsigned cmd,
- unsigned long arg)
+static long tzic_ioctl(struct file *file, unsigned cmd, unsigned long arg)
{
int ret = 0;
@@ -68,9 +113,20 @@ static long tzic_ioctl(struct file *file, unsigned cmd,
return -1;
}
- LOG(KERN_INFO "set_fuse");
- exynos_smc1(SMC_CMD_STORE_BINFO, 0x80010001, 0, 0);
- exynos_smc1(SMC_CMD_STORE_BINFO, 0x00000001, 0, 0);
+ if (cmd == TZIC_IOCTL_SET_FUSE_REQ) {
+ LOG(KERN_INFO "set_fuse");
+ exynos_smc1(SMC_CMD_STORE_BINFO, 0x80010001, 0, 0);
+ exynos_smc1(SMC_CMD_STORE_BINFO, 0x00000001, 0, 0);
+ } else if (cmd == TZIC_IOCTL_GET_FUSE_REQ) {
+ LOG(KERN_INFO "get_fuse");
+#if defined(CONFIG_FELICA)
+ exynos_smc_read_oemflag(0x80010001, (u32 *) arg);
+#else
+ LOG(KERN_INFO "get_fuse not supported : CONFIG_FELICA");
+#endif
+ } else {
+ LOG(KERN_INFO "command error");
+ }
gotoAllCpu();
@@ -101,7 +157,7 @@ static int __init tzic_init(void)
}
class_dev = device_create(driver_class, NULL, tzic_device_no, NULL,
- TZIC_DEV);
+ TZIC_DEV);
if (!class_dev) {
LOG(KERN_INFO "class_device_create failed %d", rc);
rc = -ENOMEM;
@@ -119,11 +175,11 @@ static int __init tzic_init(void)
return 0;
-class_device_destroy:
+ class_device_destroy:
device_destroy(driver_class, tzic_device_no);
-class_destroy:
+ class_destroy:
class_destroy(driver_class);
-unregister_chrdev_region:
+ unregister_chrdev_region:
unregister_chrdev_region(tzic_device_no, 1);
return rc;
}
@@ -137,42 +193,36 @@ static void __exit tzic_exit(void)
static int gotoCpu0(void)
{
- int ret = 0;
- struct cpumask mask = CPU_MASK_CPU0;
+ int ret = 0;
+ struct cpumask mask = CPU_MASK_CPU0;
LOG(KERN_INFO "System has %d CPU's, we are on CPU #%d\n"
- "\tBinding this process to CPU #0.\n"
- "\tactive mask is %lx, setting it to mask=%lx\n",
- nr_cpu_ids,
- raw_smp_processor_id(),
- cpu_active_mask->bits[0],
- mask.bits[0]);
+ "\tBinding this process to CPU #0.\n"
+ "\tactive mask is %lx, setting it to mask=%lx\n",
+ nr_cpu_ids,
+ raw_smp_processor_id(), cpu_active_mask->bits[0], mask.bits[0]);
ret = set_cpus_allowed_ptr(current, &mask);
if (0 != ret)
LOG(KERN_INFO "set_cpus_allowed_ptr=%d.\n", ret);
- LOG(KERN_INFO "And now we are on CPU #%d",
- raw_smp_processor_id());
+ LOG(KERN_INFO "And now we are on CPU #%d", raw_smp_processor_id());
return ret;
}
static int gotoAllCpu(void)
{
- int ret = 0;
- struct cpumask mask = CPU_MASK_ALL;
+ int ret = 0;
+ struct cpumask mask = CPU_MASK_ALL;
LOG(KERN_INFO "System has %d CPU's, we are on CPU #%d\n"
- "\tBinding this process to CPU #0.\n"
- "\tactive mask is %lx, setting it to mask=%lx\n",
- nr_cpu_ids,
- raw_smp_processor_id(),
- cpu_active_mask->bits[0],
- mask.bits[0]);
+ "\tBinding this process to CPU #0.\n"
+ "\tactive mask is %lx, setting it to mask=%lx\n",
+ nr_cpu_ids,
+ raw_smp_processor_id(), cpu_active_mask->bits[0], mask.bits[0]);
ret = set_cpus_allowed_ptr(current, &mask);
if (0 != ret)
LOG(KERN_INFO "set_cpus_allowed_ptr=%d.\n", ret);
- LOG(KERN_INFO "And now we are on CPU #%d",
- raw_smp_processor_id());
+ LOG(KERN_INFO "And now we are on CPU #%d", raw_smp_processor_id());
return ret;
}
@@ -183,4 +233,3 @@ MODULE_VERSION("1.00");
module_init(tzic_init);
module_exit(tzic_exit);
-
diff --git a/drivers/misc/usb3503_otg_conn.c b/drivers/misc/usb3503_otg_conn.c
new file mode 100644
index 0000000..f6e31a8
--- /dev/null
+++ b/drivers/misc/usb3503_otg_conn.c
@@ -0,0 +1,378 @@
+/*
+ * drivers/misc/usb3503.c - usb3503 usb hub driver
+ *
+ * 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/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/platform_data/usb3503_otg_conn.h>
+#include <linux/delay.h>
+
+static struct i2c_client *this_client;
+static struct usb3503_platform_data *pdata;
+static int current_mode;
+
+/* DEFINE RESISTERs */
+#define CFG1_REG 0x06
+#define SP_ILOCK_REG 0xE7
+#define CFGP_REG 0xEE
+#define PDS_REG 0x0A
+
+/*
+ * 0x06; only AP (disable CP and WiMax)
+ * 0x0A; only CP (disable AP and Wimax)
+ * 0x00; enable all port
+ *
+ * bit : 3 2 1 0
+ * AP, CP, WIMAX, RESERVED
+ */
+static int hub_port = (0x3 << 2);
+
+static ssize_t mode_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+static ssize_t mode_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size);
+static ssize_t port_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+static ssize_t port_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size);
+int usb3503_register_write(char reg, char data);
+int usb3503_register_read(char reg, char *data);
+int usb3503_set_mode(int mode);
+
+static DEVICE_ATTR(mode, 0664, mode_show, mode_store);
+
+static ssize_t mode_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ if (current_mode == USB_3503_MODE_HUB)
+ return sprintf(buf, "%s", "hub");
+ else if (current_mode == USB_3503_MODE_STANDBY)
+ return sprintf(buf, "%s", "standby");
+
+ return 0;
+}
+
+static ssize_t mode_store(
+ struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ if (!strncmp(buf, "hub", 3)) {
+ usb3503_set_mode(USB_3503_MODE_HUB);
+ pr_debug("usb3503 mode set to hub\n");
+ } else if (!strncmp(buf, "standby", 7)) {
+ usb3503_set_mode(USB_3503_MODE_STANDBY);
+ pr_debug("usb3503 mode set to standby\n");
+ }
+ return size;
+}
+
+static DEVICE_ATTR(port, 0664, port_show, port_store);
+
+static ssize_t port_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "hub_port=0x%x", hub_port);
+}
+
+static ssize_t port_store(
+ struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ return 0;
+/*
+ if (!strncmp(buf, "disable_cp", 10)) {
+ hub_port = (0x1 << 2) | hub_port ;
+ pr_debug("usb3803 port disable cp\n");
+ } else if (!strncmp(buf, "disable_wimax", 13)) {
+ hub_port = (0x1 << 1) | hub_port ;
+ pr_debug("usb3803 port disable wimaxb\n");
+ } else if (!strncmp(buf, "disable_ap", 10)) {
+ hub_port = (0x1 << 3) | hub_port ;
+ pr_debug("usb3803 port disable ap\n");
+ } else if (!strncmp(buf, "enable_cp", 9)) {
+ hub_port = ~(0x1 << 2) & hub_port ;
+ pr_debug("usb3803 port enable cp\n");
+ } else if (!strncmp(buf, "enable_wimax", 12)) {
+ hub_port = ~(0x1 << 1) & hub_port ;
+ pr_debug("usb3803 port enable wimaxb\n");
+ } else if (!strncmp(buf, "enable_ap", 9)) {
+ hub_port = ~(0x1 << 3) & hub_port ;
+ pr_debug("usb3803 port enable ap\n");
+ }
+
+ if(current_mode == USB_3503_MODE_HUB)
+ usb3503_set_mode(USB_3503_MODE_HUB);
+ pr_debug("usb3503 mode setting (%s)\n", buf);
+
+ return size;
+*/
+}
+
+int usb3503_register_write(char reg, char data)
+{
+ int ret;
+ char buf[2];
+ struct i2c_msg msg[] = {
+ {
+ .addr = this_client->addr,
+ .flags = 0,
+ .len = 2,
+ .buf = buf,
+ },
+ };
+
+ buf[0] = reg;
+ buf[1] = data;
+
+ ret = i2c_transfer(this_client->adapter, msg, 1);
+ if (ret < 0)
+ pr_err("[%s] reg:0x%x data:0x%x write failed\n", \
+ __func__, reg, data);
+
+ return ret;
+}
+
+int usb3503_register_read(char reg, char *data)
+{
+ int ret;
+
+ struct i2c_msg msgs[] = {
+ {
+ .addr = this_client->addr,
+ .flags = 0,
+ .len = 1,
+ .buf = &reg,
+ },
+ {
+ .addr = this_client->addr,
+ .flags = I2C_M_RD,
+ .len = 1,
+ .buf = data,
+ },
+ };
+
+ ret = i2c_transfer(this_client->adapter, msgs, 2);
+
+ if (ret < 0)
+ pr_err("[%s] 0x%x read failed\n", __func__, reg);
+ return ret;
+}
+
+int usb3503_set_mode(int mode)
+{
+ int ret = 0;
+ printk("[%s]%d... mode = %d\n", \
+ __func__, __LINE__, mode);
+ switch (mode) {
+ case USB_3503_MODE_HUB:
+ pdata->reset_n(0);
+ msleep(20);
+ /* reset assert */
+ pdata->reset_n(1);
+
+ /* there is a major glitch in the
+ ES version of USB3803 */
+ /* below is the work-around. CS version
+ doesn't need this code! */
+ if (pdata->es_ver) {
+ int i;
+ char data;
+ msleep(5);
+
+ /* Step 1. Write value 0x03 to register 0xE7
+ after RESET_N transitioning to high. */
+ for (i = 0; i < 3; i++) {
+ /*pr_info("[%s] write SP_ILOCK_REG
+ (attempt %d)\n", __func__, i);*/
+ usb3503_register_write(SP_ILOCK_REG,
+ 0x03);
+ usb3503_register_read(SP_ILOCK_REG,
+ &data);
+ /*pr_info("[%s] read SP_ILOCK_REG :
+ 0x%x\n", __func__, data);*/
+
+ if (data == 0x3)
+ break;
+ }
+ if (i >= 3) {
+ pr_crit("[%s] SP_ILOCK_REG set failed!" \
+ "aborted!\n", __func__);
+ ret = -1;
+ break;
+ }
+ /* Step 2. Set bit 7 (ClkSusp)
+ of register 0xEE to high. */
+ /*usb3503_register_read(CFGP_REG, &data);*/
+ /*pr_info("[%s] read CFGP_REG :
+ 0x%x (default)\n", __func__, data);*/
+ data |= 0x80;
+ usb3503_register_write(CFGP_REG, data);
+ /*usb3503_register_read(CFGP_REG, &data);*/
+ /*pr_info("[%s] read CFGP_REG :
+ 0x%x\n", __func__, data);*/
+
+ /* Step 2.5
+ * Port Disable For Self Powered Operation
+ */
+ /*usb3503_register_read(PDS_REG, &data);*/
+ /*pr_info("[%s] read PDS_REG :
+ 0x%x (default)\n", __func__, data);*/
+ usb3503_register_write(PDS_REG, hub_port);
+ /*usb3503_register_read(PDS_REG, &data);*/
+ /*pr_info("[%s] hub_port : 0x%x\n",
+ __func__, hub_port);*/
+ /*pr_info("[%s] read PDS_REG :
+ 0x%x\n", __func__, data);*/
+
+ /* Step 3. Set bit 7 (SELF_BUS_PWR)
+ of register 0x06 to high. */
+ /*usb3503_register_read(CFG1_REG, &data);*/
+ /*pr_info("[%s] read CFG1_REG :
+ 0x%x (default)\n", __func__, data);*/
+ data |= 0x80;
+ usb3503_register_write(CFG1_REG, data);
+ /*usb3503_register_read(CFG1_REG, &data);*/
+ /*pr_info("[%s] read CFG1_REG :
+ 0x%x\n", __func__, data);*/
+
+ /* Step 4. Clear bit 0 (config_n) &
+ bit 1 (connect_n) of register 0xE7. */
+ usb3503_register_write(SP_ILOCK_REG, 0x00);
+ /*usb3503_register_read(SP_ILOCK_REG, &data);*/
+ /*pr_info("[%s] read SP_ILOCK_REG :
+ 0x%x\n", __func__, data);*/
+ current_mode = mode;
+ }
+ break;
+ case USB_3503_MODE_STANDBY:
+ pdata->reset_n(0);
+ current_mode = mode;
+ break;
+
+ default:
+ pr_err("[%s] Invalid mode %d\n", __func__, mode);
+ break;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL(usb3503_set_mode);
+
+int usb3503_suspend(struct i2c_client *client, pm_message_t mesg)
+{
+ pdata->port_enable(2, 0);
+
+ pdata->reset_n(0);
+ pr_info("USB3503 suspended\n");
+ return 0;
+}
+
+int usb3503_resume(struct i2c_client *client)
+{
+ if (current_mode == USB_3503_MODE_HUB)
+ pr_info("USB3503 skip hub mode setting\n");
+ else
+ usb3503_set_mode(current_mode);
+ if (current_mode == USB_3503_MODE_HUB)
+ pr_info("USB3503 resumed to mode %s\n", "hub");
+ else if (current_mode == USB_3503_MODE_STANDBY)
+ pr_info("USB3503 resumed to mode %s\n", "standby");
+ return 0;
+}
+
+
+int usb3503_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ int err = 0;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ err = -ENODEV;
+ goto exit_check_functionality_failed;
+ }
+
+ pdata = client->dev.platform_data;
+ if (pdata == NULL) {
+ pr_err("USB3503 device's platform data is NULL!\n");
+ err = -ENODEV;
+ goto exit_pdata_null;
+ }
+
+ pdata->hw_config();
+
+ this_client = client;
+
+ if (pdata->init_needed)
+ usb3503_set_mode(pdata->inital_mode);
+
+ current_mode = pdata->inital_mode;
+
+ err = device_create_file(&client->dev, &dev_attr_mode);
+ err = device_create_file(&client->dev, &dev_attr_port);
+
+ if (current_mode == USB_3503_MODE_HUB)
+ printk("USB3503 probed on mode %s\n", "hub");
+ else if (current_mode == USB_3503_MODE_STANDBY)
+ printk("USB3503 probed on mode %s\n", "standby");
+
+exit_pdata_null:
+exit_check_functionality_failed:
+ return err;
+
+}
+
+static int usb3503_remove(struct i2c_client *client)
+{
+ return 0;
+}
+
+static const struct i2c_device_id usb3503_id[] = {
+ { USB3503_I2C_NAME, 0 },
+ { }
+};
+
+
+static void usb3503_shutdown(struct i2c_client *client)
+{
+ printk(KERN_ERR "[usbhub] %s() ...\n", __func__);
+ mdelay(10);
+ usb3503_set_mode(USB_3503_MODE_STANDBY);
+}
+
+static struct i2c_driver usb3503_driver = {
+ .probe = usb3503_probe,
+ .remove = usb3503_remove,
+ .suspend = usb3503_suspend,
+ .resume = usb3503_resume,
+ .shutdown = usb3503_shutdown,
+ .id_table = usb3503_id,
+ .driver = {
+ .name = USB3503_I2C_NAME,
+ },
+};
+
+static int __init usb3503_init(void)
+{
+ pr_info("USB3503 USB HUB driver init\n");
+ return i2c_add_driver(&usb3503_driver);
+}
+
+static void __exit usb3503_exit(void)
+{
+ pr_info("USB3503 USB HUB driver exit\n");
+ i2c_del_driver(&usb3503_driver);
+}
+
+module_init(usb3503_init);
+module_exit(usb3503_exit);
+
+MODULE_DESCRIPTION("USB3503 USB HUB driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/misc/usb3803.c b/drivers/misc/usb3803.c
new file mode 100644
index 0000000..5a05b38
--- /dev/null
+++ b/drivers/misc/usb3803.c
@@ -0,0 +1,390 @@
+/*
+ * drivers/misc/usb3803.c - usb3803 usb hub driver
+ *
+ * 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/i2c.h>
+#include <linux/usb3803.h>
+#include <linux/delay.h>
+
+static struct i2c_client *this_client;
+static struct usb3803_platform_data *pdata;
+static int current_mode;
+
+/* DEFINE RESISTERs */
+#define CFG1_REG 0x06
+#define SP_ILOCK_REG 0xE7
+#define CFGP_REG 0xEE
+#define PDS_REG 0x0A
+
+/*
+ * 0x06; only AP (disable CP and WiMax)
+ * 0x0A; only CP (disable AP and Wimax)
+ * 0x00; enable all port
+ *
+ * bit : 3 2 1 0
+ * AP, CP, WIMAX, RESERVED
+ */
+static int hub_port = 0x0;
+
+static ssize_t mode_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+static ssize_t mode_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size);
+static ssize_t port_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+static ssize_t port_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size);
+static int usb3803_register_write(char reg, char data);
+static int usb3803_register_read(char reg, char *data);
+
+
+static DEVICE_ATTR(mode, 0664, mode_show, mode_store);
+
+static ssize_t mode_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ if (current_mode == USB_3803_MODE_HUB)
+ return sprintf(buf, "%s", "hub");
+ else if (current_mode == USB_3803_MODE_BYPASS)
+ return sprintf(buf, "%s", "bypass");
+ else if (current_mode == USB_3803_MODE_STANDBY)
+ return sprintf(buf, "%s", "standby");
+
+ return 0;
+}
+
+static ssize_t mode_store(
+ struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ if (!strncmp(buf, "hub", 3)) {
+ usb3803_set_mode(USB_3803_MODE_HUB);
+ pr_debug("usb3803 mode set to hub\n");
+ } else if (!strncmp(buf, "bypass", 6)) {
+ usb3803_set_mode(USB_3803_MODE_BYPASS);
+ pr_debug("usb3803 mode set to bypass\n");
+ } else if (!strncmp(buf, "standby", 7)) {
+ usb3803_set_mode(USB_3803_MODE_STANDBY);
+ pr_debug("usb3803 mode set to standby\n");
+ }
+ return size;
+}
+
+static DEVICE_ATTR(port, 0664, port_show, port_store);
+
+static ssize_t port_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "hub_port=0x%x", hub_port);
+}
+
+static ssize_t port_store(
+ struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ if (!strncmp(buf, "disable_cp", 10)) {
+ hub_port = (0x1 << 2) | hub_port;
+ pr_debug("usb3803 port disable cp\n");
+ } else if (!strncmp(buf, "disable_wimax", 13)) {
+ hub_port = (0x1 << 1) | hub_port;
+ pr_debug("usb3803 port disable wimaxb\n");
+ } else if (!strncmp(buf, "disable_ap", 10)) {
+ hub_port = (0x1 << 3) | hub_port;
+ pr_debug("usb3803 port disable ap\n");
+ } else if (!strncmp(buf, "enable_cp", 9)) {
+ hub_port = ~(0x1 << 2) & hub_port;
+ pr_debug("usb3803 port enable cp\n");
+ } else if (!strncmp(buf, "enable_wimax", 12)) {
+ hub_port = ~(0x1 << 1) & hub_port;
+ pr_debug("usb3803 port enable wimaxb\n");
+ } else if (!strncmp(buf, "enable_ap", 9)) {
+ hub_port = ~(0x1 << 3) & hub_port;
+ pr_debug("usb3803 port enable ap\n");
+ }
+
+ if (current_mode == USB_3803_MODE_HUB)
+ usb3803_set_mode(USB_3803_MODE_HUB);
+ pr_debug("usb3803 mode setting (%s)\n", buf);
+
+ return size;
+}
+
+static int usb3803_register_write(char reg, char data)
+{
+ int ret;
+ char buf[2];
+ struct i2c_msg msg[] = {
+ {
+ .addr = this_client->addr,
+ .flags = 0,
+ .len = 2,
+ .buf = buf,
+ },
+ };
+
+ buf[0] = reg;
+ buf[1] = data;
+
+ ret = i2c_transfer(this_client->adapter, msg, 1);
+
+ if (ret < 0)
+ pr_err("[%s] reg:0x%x data:0x%x write failed\n",
+ __func__, reg, data);
+
+ return ret;
+}
+
+static int usb3803_register_read(char reg, char *data)
+{
+ int ret;
+
+ struct i2c_msg msgs[] = {
+ {
+ .addr = this_client->addr,
+ .flags = 0,
+ .len = 1,
+ .buf = &reg,
+ },
+ {
+ .addr = this_client->addr,
+ .flags = I2C_M_RD,
+ .len = 1,
+ .buf = data,
+ },
+ };
+
+ ret = i2c_transfer(this_client->adapter, msgs, 2);
+
+ if (ret < 0)
+ pr_err("[%s] 0x%x read failed\n", __func__, reg);
+
+ return ret;
+}
+
+int usb3803_set_mode(int mode)
+{
+ int ret = 0;
+
+ switch (mode) {
+ case USB_3803_MODE_HUB:
+ pdata->reset_n(0);
+ msleep(20);
+
+ /* enable clock */
+ pdata->clock_en(1);
+ /* reset assert */
+ pdata->reset_n(1);
+ /* bypass mode disable */
+ pdata->bypass_n(1);
+
+ /* there is a major glitch in the ES version of USB3803 */
+ /* below is the work-around. CS version doesn't
+ need this code! */
+ if (pdata->es_ver) {
+ int i;
+ char data;
+ usleep_range(5000, 5000);
+
+ /* Step 1. Write value 0x03 to register
+ 0xE7 after RESET_N transitioning to high. */
+ for (i = 0; i < 3; i++) {
+ pr_info("[%s] write SP_ILOCK_REG (attempt %d)\n",
+ __func__, i);
+ usb3803_register_write(SP_ILOCK_REG, 0x03);
+ usb3803_register_read(SP_ILOCK_REG, &data);
+ pr_info("[%s] read SP_ILOCK_REG : 0x%x\n",
+ __func__, data);
+
+ if (data == 0x3)
+ break;
+ }
+ if (i >= 3) {
+ pr_crit("[%s] SP_ILOCK_REG set failed! aborted!\n",
+ __func__);
+ ret = -1;
+ break;
+ }
+
+ /* Step 2. Set bit 7 (ClkSusp) of register 0xEE
+ to high. */
+ usb3803_register_read(CFGP_REG, &data);
+ pr_info("[%s] read CFGP_REG : 0x%x (default)\n",
+ __func__, data);
+ data |= 0x80;
+ usb3803_register_write(CFGP_REG, data);
+ usb3803_register_read(CFGP_REG, &data);
+ pr_info("[%s] read CFGP_REG : 0x%x\n", __func__, data);
+
+ /* Step 2.5
+ * Port Disable For Self Powered Operation
+ */
+ usb3803_register_read(PDS_REG, &data);
+ pr_info("[%s] read PDS_REG : 0x%x (default)\n"
+ , __func__, data);
+ usb3803_register_write(PDS_REG, hub_port);
+ usb3803_register_read(PDS_REG, &data);
+ pr_info("[%s] hub_port : 0x%x\n"
+ , __func__, hub_port);
+ pr_info("[%s] read PDS_REG : 0x%x\n"
+ , __func__, data);
+
+ /* Step 3. Set bit 7 (SELF_BUS_PWR) of
+ register 0x06 to high.*/
+ usb3803_register_read(CFG1_REG, &data);
+ pr_info("[%s] read CFG1_REG : 0x%x (default)\n",
+ __func__, data);
+ data |= 0x80;
+ usb3803_register_write(CFG1_REG, data);
+ usb3803_register_read(CFG1_REG, &data);
+ pr_info("[%s] read CFG1_REG : 0x%x\n", __func__, data);
+
+ /* Step 4. Clear bit 0 (config_n) & bit 1
+ (connect_n) of register 0xE7. */
+ usb3803_register_write(SP_ILOCK_REG, 0x00);
+ usb3803_register_read(SP_ILOCK_REG, &data);
+ pr_info("[%s] read SP_ILOCK_REG : 0x%x\n",
+ __func__, data);
+ current_mode = mode;
+ }
+ break;
+
+ case USB_3803_MODE_BYPASS:
+ /* disable clock */
+ pdata->clock_en(0);
+ /* reset assert */
+ pdata->reset_n(1);
+ /* bypass mode enable */
+ pdata->bypass_n(0);
+ current_mode = mode;
+ break;
+
+ case USB_3803_MODE_STANDBY:
+ pdata->reset_n(0);
+ /* disable clock */
+ pdata->clock_en(0);
+ pdata->bypass_n(0);
+ current_mode = mode;
+ break;
+
+ default:
+ pr_err("[%s] Invalid mode %d\n", __func__, mode);
+ break;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL(usb3803_set_mode);
+
+int usb3803_suspend(struct i2c_client *client, pm_message_t mesg)
+{
+ pdata->reset_n(0);
+ pdata->clock_en(0);
+
+ pr_info("USB3803 suspended\n");
+
+ return 0;
+}
+
+int usb3803_resume(struct i2c_client *client)
+{
+ if (current_mode == USB_3803_MODE_HUB)
+ pr_info("USB3803 skip hub mode setting\n");
+ else
+ usb3803_set_mode(current_mode);
+ if (current_mode == USB_3803_MODE_HUB)
+ pr_info("USB3803 resumed to mode %s\n", "hub");
+ else if (current_mode == USB_3803_MODE_BYPASS)
+ pr_info("USB3803 resumed to mode %s\n", "bypass");
+ else if (current_mode == USB_3803_MODE_STANDBY)
+ pr_info("USB3803 resumed to mode %s\n", "standby");
+ return 0;
+}
+
+
+int usb3803_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ int err = 0;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ err = -ENODEV;
+ goto exit_check_functionality_failed;
+ }
+
+ pdata = client->dev.platform_data;
+ if (pdata == NULL) {
+ pr_err("USB3803 device's platform data is NULL!\n");
+ err = -ENODEV;
+ goto exit_pdata_null;
+ }
+
+ pdata->hw_config();
+
+ this_client = client;
+
+ if (pdata->init_needed)
+ usb3803_set_mode(pdata->inital_mode);
+
+ current_mode = pdata->inital_mode;
+
+ err = device_create_file(&client->dev, &dev_attr_mode);
+ err = device_create_file(&client->dev, &dev_attr_port);
+
+ if (current_mode == USB_3803_MODE_HUB)
+ pr_info("USB3803 probed on mode %s\n", "hub");
+ else if (current_mode == USB_3803_MODE_BYPASS)
+ pr_info("USB3803 probed on mode %s\n", "bypass");
+ else if (current_mode == USB_3803_MODE_STANDBY)
+ pr_info("USB3803 probed on mode %s\n", "standby");
+
+ exit_pdata_null:
+ exit_check_functionality_failed:
+ return err;
+
+}
+
+static int usb3803_remove(struct i2c_client *client)
+{
+ return 0;
+}
+static const struct i2c_device_id usb3803_id[] = {
+ { USB3803_I2C_NAME, 0 },
+ { }
+};
+
+static struct i2c_driver usb3803_driver = {
+ .probe = usb3803_probe,
+ .remove = usb3803_remove,
+ .suspend = usb3803_suspend,
+ .resume = usb3803_resume,
+ .id_table = usb3803_id,
+ .driver = {
+ .name = USB3803_I2C_NAME,
+ },
+};
+
+static int __init usb3803_init(void)
+{
+ pr_info("USB3803 USB HUB driver init\n");
+ return i2c_add_driver(&usb3803_driver);
+}
+
+static void __exit usb3803_exit(void)
+{
+ pr_info("USB3803 USB HUB driver exit\n");
+ i2c_del_driver(&usb3803_driver);
+}
+
+module_init(usb3803_init);
+module_exit(usb3803_exit);
+
+MODULE_DESCRIPTION("USB3803 USB HUB driver");
+MODULE_LICENSE("GPL");