aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/misc/modem_if/modem_link_device_dpram_ext_op.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/misc/modem_if/modem_link_device_dpram_ext_op.c')
-rw-r--r--drivers/misc/modem_if/modem_link_device_dpram_ext_op.c1619
1 files changed, 1254 insertions, 365 deletions
diff --git a/drivers/misc/modem_if/modem_link_device_dpram_ext_op.c b/drivers/misc/modem_if/modem_link_device_dpram_ext_op.c
index 1475a46..edaf6e5 100644
--- a/drivers/misc/modem_if/modem_link_device_dpram_ext_op.c
+++ b/drivers/misc/modem_if/modem_link_device_dpram_ext_op.c
@@ -25,103 +25,82 @@
#include <linux/if_arp.h>
#include <linux/platform_device.h>
#include <linux/kallsyms.h>
-#include <linux/platform_data/modem.h>
+#include <linux/suspend.h>
+#include <plat/gpio-cfg.h>
+#include <mach/gpio.h>
+#include "modem.h"
#include "modem_prj.h"
-#include "modem_link_device_dpram.h"
#include "modem_utils.h"
+#include "modem_link_device_dpram.h"
-#if defined(CONFIG_LTE_MODEM_CMC221)
-/*
-** For host (flashless) booting via DPRAM
-*/
-#define CMC22x_AP_BOOT_DOWN_DONE 0x54329876
-#define CMC22x_CP_REQ_MAIN_BIN 0xA5A5A5A5
-#define CMC22x_CP_REQ_NV_DATA 0x5A5A5A5A
-#define CMC22x_CP_DUMP_MAGIC 0xDEADDEAD
-
-#define CMC22x_HOST_DOWN_START 0x1234
-#define CMC22x_HOST_DOWN_END 0x4321
-#define CMC22x_REG_NV_DOWN_END 0xABCD
-#define CMC22x_CAL_NV_DOWN_END 0xDCBA
-
-#define CMC22x_1ST_BUFF_READY 0xAAAA
-#define CMC22x_2ND_BUFF_READY 0xBBBB
-#define CMC22x_1ST_BUFF_FULL 0x1111
-#define CMC22x_2ND_BUFF_FULL 0x2222
-
-#define CMC22x_CP_RECV_NV_END 0x8888
-#define CMC22x_CP_CAL_OK 0x4F4B
-#define CMC22x_CP_CAL_BAD 0x4552
-#define CMC22x_CP_DUMP_END 0xFADE
-
-#define CMC22x_DUMP_BUFF_SIZE 8192 /* 8 KB */
-#endif
-
-#if defined(CONFIG_CDMA_MODEM_CBP72)
-static void cbp72_init_boot_map(struct dpram_link_device *dpld)
+#if defined(CONFIG_CDMA_MODEM_CBP72) || defined(CONFIG_CDMA_MODEM_CBP82)
+static void cbp_init_boot_map(struct dpram_link_device *dpld)
{
- struct dpram_boot_map *bt_map = &dpld->bt_map;
+ struct memif_boot_map *bt_map = &dpld->bt_map;
- bt_map->magic = (u32 *)dpld->dp_base;
- bt_map->buff = (u8 *)(dpld->dp_base + DP_BOOT_BUFF_OFFSET);
- bt_map->size = dpld->dp_size - 4;
+ bt_map->magic = (u32 *)dpld->base;
+ bt_map->buff = (u8 *)(dpld->base + DP_BOOT_BUFF_OFFSET);
+ bt_map->space = dpld->size - 4;
}
-static void cbp72_init_dl_map(struct dpram_link_device *dpld)
+static void cbp_init_dl_map(struct dpram_link_device *dpld)
{
- dpld->dl_map.magic = (u32 *)dpld->dp_base;
- dpld->dl_map.buff = (u8 *)(dpld->dp_base + DP_DLOAD_BUFF_OFFSET);
+ dpld->dl_map.magic = (u32 *)dpld->base;
+ dpld->dl_map.buff = (u8 *)(dpld->base + DP_DLOAD_BUFF_OFFSET);
}
-static int _cbp72_edpram_wait_resp(struct dpram_link_device *dpld, u32 resp)
+static int cbp_udl_wait_resp(struct dpram_link_device *dpld, u32 resp)
{
- struct link_device *ld = &dpld->ld;
int ret;
- int int2cp;
+ int int2ap;
- ret = wait_for_completion_interruptible_timeout(
- &dpld->udl_cmd_complete, UDL_TIMEOUT);
+ mif_debug("wait for 0x%04X\n", resp);
+ ret = wait_for_completion_timeout(&dpld->udl_cmpl, UDL_TIMEOUT);
if (!ret) {
- mif_info("%s: ERR! No UDL_CMD_RESP!!!\n", ld->name);
- return -ENXIO;
+ mif_info("ERR! No UDL_CMD_RESP!!!\n");
+ return -EIO;
}
- int2cp = dpld->recv_intr(dpld);
- mif_debug("%s: int2cp = 0x%x\n", ld->name, int2cp);
- if (resp == int2cp || int2cp == 0xA700)
- return int2cp;
- else
+ int2ap = dpld->recv_intr(dpld);
+ if (resp == int2ap || int2ap == CMD_UL_RECV_DONE_REQ) {
+ mif_debug("int2ap = 0x%04X\n", int2ap);
+ return int2ap;
+ } else {
+ mif_err("ERR! int2ap 0x%04X != resp 0x%04X\n", int2ap, resp);
return -EINVAL;
+ }
}
-static int _cbp72_edpram_download_bin(struct dpram_link_device *dpld,
+static int cbp_xmit_binary(struct dpram_link_device *dpld,
struct sk_buff *skb)
{
- struct link_device *ld = &dpld->ld;
struct dpram_boot_frame *bf = (struct dpram_boot_frame *)skb->data;
u8 __iomem *buff = dpld->bt_map.buff;
int err = 0;
- if (bf->len > dpld->bt_map.size) {
- mif_info("%s: ERR! Out of DPRAM boundary\n", ld->name);
- err = -EINVAL;
+ if (bf->len > dpld->bt_map.space) {
+ mif_info("ERR! Out of DPRAM boundary\n");
+ err = -ERANGE;
goto exit;
}
if (bf->len)
- memcpy(buff, bf->data, bf->len);
+ memcpy16_to_io(buff, bf->data, bf->len);
- init_completion(&dpld->udl_cmd_complete);
+#ifdef CONFIG_CDMA_MODEM_CBP72
+ init_completion(&dpld->udl_cmpl);
+#endif
- if (bf->req)
+ if (bf->req) {
dpld->send_intr(dpld, (u16)bf->req);
+ mif_debug("send intr 0x%04X\n", (u16)bf->req);
+ }
if (bf->resp) {
- err = _cbp72_edpram_wait_resp(dpld, bf->resp);
+ err = cbp_udl_wait_resp(dpld, bf->resp);
if (err < 0) {
- mif_info("%s: ERR! wait_response fail (%d)\n",
- ld->name, err);
+ mif_err("ERR! cbp_udl_wait_resp fail (err %d)\n", err);
goto exit;
} else if (err == bf->resp) {
err = skb->len;
@@ -133,24 +112,14 @@ exit:
return err;
}
-static int cbp72_download_binary(struct dpram_link_device *dpld,
- struct sk_buff *skb)
-{
- if (dpld->dp_type == EXT_DPRAM)
- return _cbp72_edpram_download_bin(dpld, skb);
- else
- return -ENODEV;
-}
-
-static int cbp72_dump_start(struct dpram_link_device *dpld)
+static int cbp_dump_start(struct dpram_link_device *dpld)
{
- struct link_device *ld = &dpld->ld;
u8 *dest = dpld->ul_map.buff;
int ret;
- ld->mode = LINK_MODE_ULOAD;
+ dpld->ld.mode = LINK_MODE_ULOAD;
- ret = del_timer(&dpld->dump_timer);
+ ret = del_timer(&dpld->crash_timer);
wake_lock(&dpld->wlock);
iowrite32(DP_MAGIC_UMDL, dpld->ul_map.magic);
@@ -161,53 +130,65 @@ static int cbp72_dump_start(struct dpram_link_device *dpld)
iowrite8((u8)0x0, dest + 3);
iowrite8((u8)END_FLAG, dest + 4);
- init_completion(&dpld->dump_start_complete);
+ init_completion(&dpld->crash_cmpl);
return 0;
}
-static int _cbp72_edpram_upload(struct dpram_link_device *dpld,
- struct dpram_dump_arg *dump, unsigned char __user *target)
+static int cbp_dump_update(struct dpram_link_device *dpld, unsigned long arg)
{
- struct link_device *ld = &dpld->ld;
- struct ul_header header;
+ struct dpram_dump_arg dump;
+ struct dpram_udl_header header;
+ unsigned char __user *target = (unsigned char __user *)arg;
+ int err = 0;
+ int resp = 0;
u8 *dest = NULL;
u8 *buff = NULL;
- u16 plen = 0;
- int err = 0;
- int ret = 0;
+ u8 *header_buff = NULL;
int buff_size = 0;
+ u16 plen = 0;
- mif_debug("\n");
-
- init_completion(&dpld->udl_cmd_complete);
-
- mif_debug("%s: req %x, resp %x", ld->name, dump->req, dump->resp);
+ err = copy_from_user(&dump, (void __user *)arg, sizeof(dump));
+ if (err < 0) {
+ mif_err("ERR! ARG copy_from_user fail (err %d)\n", err);
+ goto exit;
+ }
+ mif_debug("req %x, resp %x", dump.req, dump.resp);
- if (dump->req)
- dpld->send_intr(dpld, (u16)dump->req);
+ if (dump.req)
+ dpld->send_intr(dpld, (u16)dump.req);
- if (dump->resp) {
- err = _cbp72_edpram_wait_resp(dpld, dump->resp);
+ if (dump.resp) {
+ resp = err = cbp_udl_wait_resp(dpld, dump.resp);
if (err < 0) {
- mif_info("%s: ERR! wait_response fail (%d)\n",
- ld->name, err);
+ mif_info("ERR! wait_response fail (err %d)\n", err);
goto exit;
}
}
- if (dump->cmd)
- return err;
+ if (dump.cmd)
+ goto exit;
dest = (u8 *)dpld->ul_map.buff;
- header.bop = *(u8 *)(dest);
- header.total_frame = *(u16 *)(dest + 1);
- header.curr_frame = *(u16 *)(dest + 3);
- header.len = *(u16 *)(dest + 5);
+ header_buff = vmalloc(sizeof(struct dpram_udl_header));
+ if (!header_buff) {
+ err = -ENOMEM;
+ goto exit;
+ }
+
+ memcpy16_from_io(header_buff, dest, sizeof(struct dpram_udl_header));
+
+ header.bop = *(u8 *)(header_buff);
+ header.num_frames = *(u16 *)(header_buff + 1);
+ header.curr_frame = *(u16 *)(header_buff + 3);
+ header.len = *(u16 *)(header_buff + 5);
+#ifdef CONFIG_CDMA_MODEM_CBP82
+ header.pad = *(u8 *)(header_buff + 7);
+#endif
- mif_debug("%s: total frame:%d, current frame:%d, data len:%d\n",
- ld->name, header.total_frame, header.curr_frame, header.len);
+ mif_debug("total frames:%d, current frame:%d, data len:%d\n",
+ header.num_frames, header.curr_frame, header.len);
plen = min_t(u16, header.len, DP_DEFAULT_DUMP_LEN);
@@ -217,22 +198,26 @@ static int _cbp72_edpram_upload(struct dpram_link_device *dpld,
goto exit;
}
- memcpy(buff, dest + sizeof(struct ul_header), plen);
- ret = copy_to_user(dump->buff, buff, plen);
- if (ret < 0) {
- mif_info("%s: ERR! dump copy_to_user fail\n", ld->name);
+ memcpy16_from_io(buff, dest + sizeof(struct dpram_udl_header), plen);
+ err = copy_to_user(dump.buff, buff, plen);
+ if (err) {
+ mif_info("ERR! DUMP copy_to_user fail\n");
err = -EIO;
goto exit;
}
- buff_size = plen;
- ret = copy_to_user(target + 4, &buff_size, sizeof(int));
- if (ret < 0) {
- mif_info("%s: ERR! size copy_to_user fail\n", ld->name);
+ buff_size = plen;
+ err = copy_to_user(target + 4, &buff_size, sizeof(int));
+ if (err) {
+ mif_info("ERR! SIZE copy_to_user fail\n");
err = -EIO;
goto exit;
}
+ /* Return response value */
+ if (err == 0)
+ err = resp;
+
vfree(buff);
return err;
@@ -243,82 +228,169 @@ exit:
wake_unlock(&dpld->wlock);
return err;
}
+#endif
-static int cbp72_dump_update(struct dpram_link_device *dpld, void *arg)
-{
- struct link_device *ld = &dpld->ld;
- struct dpram_dump_arg dump;
- int ret;
+#if defined(CONFIG_LTE_MODEM_CMC221)
+/*
+** For CMC221 SFR for IDPRAM
+*/
+#define CMC_INT2CP_REG 0x10 /* Interrupt to CP */
+#define CMC_INT2AP_REG 0x50
+#define CMC_CLR_INT_REG 0x28 /* Clear Interrupt to AP */
+#define CMC_RESET_REG 0x3C
+#define CMC_PUT_REG 0x40 /* AP->CP reg for hostbooting */
+#define CMC_GET_REG 0x50 /* CP->AP reg for hostbooting */
- ret = copy_from_user(&dump, (void __user *)arg, sizeof(dump));
- if (ret < 0) {
- mif_info("%s: ERR! copy_from_user fail\n", ld->name);
- return ret;
- }
+/*
+** For host (flashless) booting via DPRAM
+*/
+#define CMC22x_AP_BOOT_DOWN_DONE 0x54329876
+#define CMC22x_CP_REQ_MAIN_BIN 0xA5A5A5A5
+#define CMC22x_CP_REQ_NV_DATA 0x5A5A5A5A
+#define CMC22x_CP_DUMP_MAGIC 0xDEADDEAD
- return _cbp72_edpram_upload(dpld, &dump, (unsigned char __user *)arg);
-}
+#define CMC22x_HOST_DOWN_START 0x1234
+#define CMC22x_HOST_DOWN_END 0x4321
+#define CMC22x_REG_NV_DOWN_END 0xABCD
+#define CMC22x_CAL_NV_DOWN_END 0xDCBA
-static int cbp72_set_dl_magic(struct link_device *ld, struct io_device *iod)
-{
- struct dpram_link_device *dpld = to_dpram_link_device(ld);
+#define CMC22x_1ST_BUFF_READY 0xAAAA
+#define CMC22x_2ND_BUFF_READY 0xBBBB
+#define CMC22x_1ST_BUFF_FULL 0x1111
+#define CMC22x_2ND_BUFF_FULL 0x2222
+
+#define CMC22x_CP_RECV_NV_END 0x8888
+#define CMC22x_CP_CAL_OK 0x4F4B
+#define CMC22x_CP_CAL_BAD 0x4552
+#define CMC22x_CP_DUMP_END 0xFADE
- ld->mode = LINK_MODE_DLOAD;
+#define CMC22x_DUMP_BUFF_SIZE 8192
- iowrite32(DP_MAGIC_DMDL, dpld->dl_map.magic);
+/* CMC221 IDPRAM SFR */
+struct cmc221_idpram_sfr {
+ u16 __iomem *int2cp;
+ u16 __iomem *int2ap;
+ u16 __iomem *clr_int2ap;
+ u16 __iomem *reset;
+ u16 __iomem *msg2cp;
+ u16 __iomem *msg2ap;
+};
- return 0;
-}
+struct cmc221_boot_img {
+ char *addr;
+ int size;
+ enum cp_boot_mode mode;
+ unsigned req;
+ unsigned resp;
+};
-static int cbp72_ioctl(struct dpram_link_device *dpld, struct io_device *iod,
- unsigned int cmd, unsigned long arg)
+static struct cmc221_idpram_sfr cmc_sfr;
+
+static void cmc221_init_boot_map(struct dpram_link_device *dpld)
{
- struct link_device *ld = &dpld->ld;
- int err = 0;
+ struct memif_boot_map *bt_map = &dpld->bt_map;
- switch (cmd) {
- case IOCTL_MODEM_DL_START:
- err = cbp72_set_dl_magic(ld, iod);
- if (err < 0)
- mif_err("%s: ERR! set_dl_magic fail\n", ld->name);
- break;
+ bt_map->buff = dpld->base;
+ bt_map->space = dpld->size;
+ bt_map->req = (u32 *)(dpld->base + DP_BOOT_REQ_OFFSET);
+ bt_map->resp = (u32 *)(dpld->base + DP_BOOT_RESP_OFFSET);
+}
- default:
- mif_err("%s: ERR! invalid cmd 0x%08X\n", ld->name, cmd);
- err = -EINVAL;
- break;
- }
+static void cmc221_init_dl_map(struct dpram_link_device *dpld)
+{
+ dpld->dl_map.magic = (u32 *)dpld->base;
+ dpld->dl_map.buff = (u8 *)dpld->base;
+}
- return err;
+static void cmc221_init_ul_map(struct dpram_link_device *dpld)
+{
+ dpld->ul_map.magic = (u32 *)dpld->base;
+ dpld->ul_map.buff = (u8 *)dpld->base;
}
-#endif
-#if defined(CONFIG_LTE_MODEM_CMC221)
-static void cmc221_init_boot_map(struct dpram_link_device *dpld)
+static void cmc221_init_ipc_map(struct dpram_link_device *dpld)
{
- struct dpram_boot_map *bt_map = &dpld->bt_map;
+ struct dpram_ipc_16k_map *dpram_map;
+ struct dpram_ipc_device *dev;
+ u8 __iomem *sfr_base = dpld->sfr_base;
+
+ dpram_map = (struct dpram_ipc_16k_map *)dpld->base;
+
+ /* Magic code and access enable fields */
+ dpld->ipc_map.magic = (u16 __iomem *)&dpram_map->magic;
+ dpld->ipc_map.access = (u16 __iomem *)&dpram_map->access;
+
+ /* FMT */
+ dev = &dpld->ipc_map.dev[IPC_FMT];
+
+ strcpy(dev->name, "FMT");
+ dev->id = IPC_FMT;
+
+ dev->txq.head = (u16 __iomem *)&dpram_map->fmt_tx_head;
+ dev->txq.tail = (u16 __iomem *)&dpram_map->fmt_tx_tail;
+ dev->txq.buff = (u8 __iomem *)&dpram_map->fmt_tx_buff[0];
+ dev->txq.size = DP_16K_FMT_TX_BUFF_SZ;
+
+ dev->rxq.head = (u16 __iomem *)&dpram_map->fmt_rx_head;
+ dev->rxq.tail = (u16 __iomem *)&dpram_map->fmt_rx_tail;
+ dev->rxq.buff = (u8 __iomem *)&dpram_map->fmt_rx_buff[0];
+ dev->rxq.size = DP_16K_FMT_RX_BUFF_SZ;
+
+ dev->mask_req_ack = INT_MASK_REQ_ACK_F;
+ dev->mask_res_ack = INT_MASK_RES_ACK_F;
+ dev->mask_send = INT_MASK_SEND_F;
+
+ /* RAW */
+ dev = &dpld->ipc_map.dev[IPC_RAW];
+
+ strcpy(dev->name, "RAW");
+ dev->id = IPC_RAW;
+
+ dev->txq.head = (u16 __iomem *)&dpram_map->raw_tx_head;
+ dev->txq.tail = (u16 __iomem *)&dpram_map->raw_tx_tail;
+ dev->txq.buff = (u8 __iomem *)&dpram_map->raw_tx_buff[0];
+ dev->txq.size = DP_16K_RAW_TX_BUFF_SZ;
+
+ dev->rxq.head = (u16 __iomem *)&dpram_map->raw_rx_head;
+ dev->rxq.tail = (u16 __iomem *)&dpram_map->raw_rx_tail;
+ dev->rxq.buff = (u8 __iomem *)&dpram_map->raw_rx_buff[0];
+ dev->rxq.size = DP_16K_RAW_RX_BUFF_SZ;
+
+ dev->mask_req_ack = INT_MASK_REQ_ACK_R;
+ dev->mask_res_ack = INT_MASK_RES_ACK_R;
+ dev->mask_send = INT_MASK_SEND_R;
+
+ /* SFR */
+ cmc_sfr.int2cp = (u16 __iomem *)(sfr_base + CMC_INT2CP_REG);
+ cmc_sfr.int2ap = (u16 __iomem *)(sfr_base + CMC_INT2AP_REG);
+ cmc_sfr.clr_int2ap = (u16 __iomem *)(sfr_base + CMC_CLR_INT_REG);
+ cmc_sfr.reset = (u16 __iomem *)(sfr_base + CMC_RESET_REG);
+ cmc_sfr.msg2cp = (u16 __iomem *)(sfr_base + CMC_PUT_REG);
+ cmc_sfr.msg2ap = (u16 __iomem *)(sfr_base + CMC_GET_REG);
+
+ /* Interrupt ports */
+ dpld->ipc_map.mbx_cp2ap = cmc_sfr.int2ap;
+ dpld->ipc_map.mbx_ap2cp = cmc_sfr.int2cp;
+}
- bt_map->buff = dpld->dp_base;
- bt_map->size = dpld->dp_size;
- bt_map->req = (u32 *)(dpld->dp_base + DP_BOOT_REQ_OFFSET);
- bt_map->resp = (u32 *)(dpld->dp_base + DP_BOOT_RESP_OFFSET);
+static inline void cmc221_idpram_reset(struct dpram_link_device *dpld)
+{
+ iowrite16(1, cmc_sfr.reset);
}
-static void cmc221_init_dl_map(struct dpram_link_device *dpld)
+static inline u16 cmc221_idpram_recv_msg(struct dpram_link_device *dpld)
{
- dpld->dl_map.magic = (u32 *)dpld->dp_base;
- dpld->dl_map.buff = (u8 *)dpld->dp_base;
+ return ioread16(cmc_sfr.msg2ap);
}
-static void cmc221_init_ul_map(struct dpram_link_device *dpld)
+static inline void cmc221_idpram_send_msg(struct dpram_link_device *dpld,
+ u16 msg)
{
- dpld->ul_map.magic = (u32 *)dpld->dp_base;
- dpld->ul_map.buff = (u8 *)dpld->dp_base;
+ iowrite16(msg, cmc_sfr.msg2cp);
}
-static int _cmc221_idpram_wait_resp(struct dpram_link_device *dpld, u32 resp)
+static int cmc221_idpram_wait_resp(struct dpram_link_device *dpld, u32 resp)
{
- struct link_device *ld = &dpld->ld;
int count = 50000;
u32 rcvd = 0;
@@ -328,16 +400,14 @@ static int _cmc221_idpram_wait_resp(struct dpram_link_device *dpld, u32 resp)
if (rcvd == resp)
break;
- rcvd = dpld->dpctl->recv_msg();
+ rcvd = cmc221_idpram_recv_msg(dpld);
if (rcvd == 0x9999) {
- mif_info("%s: Invalid resp 0x%04X\n",
- ld->name, rcvd);
+ mif_info("invalid resp 0x%04X\n", rcvd);
panic("CP Crash ... BAD CRC in CP");
}
if (count-- < 0) {
- mif_info("%s: Invalid resp 0x%08X\n",
- ld->name, rcvd);
+ mif_info("invalid resp 0x%08X\n", rcvd);
return -EAGAIN;
}
@@ -345,20 +415,19 @@ static int _cmc221_idpram_wait_resp(struct dpram_link_device *dpld, u32 resp)
}
} else {
while (1) {
- rcvd = dpld->dpctl->recv_msg();
+ rcvd = cmc221_idpram_recv_msg(dpld);
if (rcvd == resp)
break;
if (resp == CMC22x_CP_RECV_NV_END &&
rcvd == CMC22x_CP_CAL_BAD) {
- mif_info("%s: CMC22x_CP_CAL_BAD\n", ld->name);
+ mif_info("invalid resp CMC22x_CP_CAL_BAD\n");
break;
}
if (count-- < 0) {
- mif_info("%s: Invalid resp 0x%04X\n",
- ld->name, rcvd);
+ mif_info("invalid resp 0x%04X\n", rcvd);
return -EAGAIN;
}
@@ -369,110 +438,104 @@ static int _cmc221_idpram_wait_resp(struct dpram_link_device *dpld, u32 resp)
return rcvd;
}
-static int _cmc221_idpram_send_boot(struct dpram_link_device *dpld, void *arg)
+static int cmc221_xmit_boot(struct dpram_link_device *dpld, unsigned long arg)
{
struct link_device *ld = &dpld->ld;
u8 __iomem *bt_buff = dpld->bt_map.buff;
- struct dpram_boot_img cp_img;
+ struct cmc221_boot_img cp_img;
u8 *img_buff = NULL;
int err = 0;
int cnt = 0;
+ mif_info("+++\n");
ld->mode = LINK_MODE_BOOT;
- dpld->dpctl->setup_speed(DPRAM_SPEED_LOW);
+ dpld->dpram->setup_speed(DPRAM_SPEED_LOW);
/* Test memory... After testing, memory is cleared. */
- if (mif_test_dpram(ld->name, bt_buff, dpld->bt_map.size) < 0) {
- mif_info("%s: ERR! mif_test_dpram fail!\n", ld->name);
+ if (mif_test_dpram(ld->name, bt_buff, dpld->bt_map.space) < 0) {
+ mif_info("ERR! mif_test_dpram fail!\n");
ld->mode = LINK_MODE_OFFLINE;
return -EIO;
}
- memset(&cp_img, 0, sizeof(struct dpram_boot_img));
+ memset(&cp_img, 0, sizeof(struct cmc221_boot_img));
/* Get information about the boot image */
- err = copy_from_user(&cp_img, arg, sizeof(cp_img));
- mif_info("%s: CP image addr = 0x%08X, size = %d\n",
- ld->name, (int)cp_img.addr, cp_img.size);
+ err = copy_from_user(&cp_img, (void __user *)arg, sizeof(cp_img));
+ mif_info("CP image addr = 0x%08X, size = %d\n",
+ (int)cp_img.addr, cp_img.size);
/* Alloc a buffer for the boot image */
- img_buff = kzalloc(dpld->bt_map.size, GFP_KERNEL);
+ img_buff = kzalloc(dpld->bt_map.space, GFP_KERNEL);
if (!img_buff) {
- mif_info("%s: ERR! kzalloc fail\n", ld->name);
+ mif_info("ERR! kzalloc fail\n");
ld->mode = LINK_MODE_OFFLINE;
return -ENOMEM;
}
/* Copy boot image from the user space to the image buffer */
- err = copy_from_user(img_buff, cp_img.addr, cp_img.size);
+ err = copy_from_user(img_buff, (void __user *)cp_img.addr, cp_img.size);
/* Copy boot image to DPRAM and verify it */
memcpy(bt_buff, img_buff, cp_img.size);
if (memcmp16_to_io(bt_buff, img_buff, cp_img.size)) {
- mif_info("%s: ERR! Boot may be broken!!!\n", ld->name);
+ mif_info("ERR! Boot may be broken!!!\n");
goto err;
}
- dpld->dpctl->reset();
+ cmc221_idpram_reset(dpld);
usleep_range(1000, 2000);
- if (cp_img.mode == HOST_BOOT_MODE_NORMAL) {
- mif_info("%s: HOST_BOOT_MODE_NORMAL\n", ld->name);
- mif_info("%s: Send req 0x%08X\n", ld->name, cp_img.req);
+ if (cp_img.mode == CP_BOOT_MODE_NORMAL) {
+ mif_info("CP_BOOT_MODE_NORMAL\n");
+ mif_info("send req 0x%08X\n", cp_img.req);
iowrite32(cp_img.req, dpld->bt_map.req);
/* Wait for cp_img.resp for up to 2 seconds */
- mif_info("%s: Wait resp 0x%08X\n", ld->name, cp_img.resp);
+ mif_info("wait resp 0x%08X\n", cp_img.resp);
while (ioread32(dpld->bt_map.resp) != cp_img.resp) {
cnt++;
usleep_range(1000, 2000);
if (cnt > 1000) {
- mif_info("%s: ERR! Invalid resp 0x%08X\n",
- ld->name, ioread32(dpld->bt_map.resp));
+ mif_info("ERR! invalid resp 0x%08X\n",
+ ioread32(dpld->bt_map.resp));
goto err;
}
}
} else {
- mif_info("%s: HOST_BOOT_MODE_DUMP\n", ld->name);
+ mif_info("CP_BOOT_MODE_DUMP\n");
}
kfree(img_buff);
- mif_info("%s: Send BOOT done\n", ld->name);
+ mif_info("send BOOT done\n");
- dpld->dpctl->setup_speed(DPRAM_SPEED_HIGH);
+ dpld->dpram->setup_speed(DPRAM_SPEED_HIGH);
+ mif_info("---\n");
return 0;
err:
ld->mode = LINK_MODE_OFFLINE;
kfree(img_buff);
- mif_info("%s: ERR! Boot send fail!!!\n", ld->name);
+ mif_err("FAIL!!!\n");
+ mif_info("---\n");
return -EIO;
}
-static int cmc221_download_boot(struct dpram_link_device *dpld, void *arg)
-{
- if (dpld->dp_type == CP_IDPRAM)
- return _cmc221_idpram_send_boot(dpld, arg);
- else
- return -ENODEV;
-}
-
-static int _cmc221_idpram_download_bin(struct dpram_link_device *dpld,
+static int cmc221_idpram_download_bin(struct dpram_link_device *dpld,
struct sk_buff *skb)
{
int err = 0;
int ret = 0;
- struct link_device *ld = &dpld->ld;
struct dpram_boot_frame *bf = (struct dpram_boot_frame *)skb->data;
u8 __iomem *buff = (dpld->bt_map.buff + bf->offset);
- if ((bf->offset + bf->len) > dpld->bt_map.size) {
- mif_info("%s: ERR! Out of DPRAM boundary\n", ld->name);
+ if ((bf->offset + bf->len) > dpld->bt_map.space) {
+ mif_info("ERR! out of DPRAM boundary\n");
err = -EINVAL;
goto exit;
}
@@ -481,17 +544,16 @@ static int _cmc221_idpram_download_bin(struct dpram_link_device *dpld,
memcpy(buff, bf->data, bf->len);
if (bf->req)
- dpld->dpctl->send_msg((u16)bf->req);
+ cmc221_idpram_send_msg(dpld, (u16)bf->req);
if (bf->resp) {
- err = _cmc221_idpram_wait_resp(dpld, bf->resp);
+ err = cmc221_idpram_wait_resp(dpld, bf->resp);
if (err < 0)
- mif_info("%s: ERR! wait_response fail (err %d)\n",
- ld->name, err);
+ mif_info("ERR! wait_resp fail (err %d)\n", err);
}
if (bf->req == CMC22x_CAL_NV_DOWN_END)
- mif_info("%s: CMC22x_CAL_NV_DOWN_END\n", ld->name);
+ mif_info("request CMC22x_CAL_NV_DOWN_END\n");
exit:
if (err < 0)
@@ -504,89 +566,84 @@ exit:
return ret;
}
-static int cmc221_download_binary(struct dpram_link_device *dpld,
+static int cmc221_xmit_binary(struct dpram_link_device *dpld,
struct sk_buff *skb)
{
- if (dpld->dp_type == CP_IDPRAM)
- return _cmc221_idpram_download_bin(dpld, skb);
+ if (dpld->type == CP_IDPRAM)
+ return cmc221_idpram_download_bin(dpld, skb);
else
return -ENODEV;
}
static int cmc221_dump_start(struct dpram_link_device *dpld)
{
- struct link_device *ld = &dpld->ld;
- int ret;
-
- ld->mode = LINK_MODE_ULOAD;
+ dpld->ld.mode = LINK_MODE_ULOAD;
- ret = del_timer(&dpld->dump_timer);
+ del_timer(&dpld->crash_timer);
wake_lock(&dpld->wlock);
- dpld->dump_rcvd = 0;
+ dpld->crash_rcvd = 0;
iowrite32(CMC22x_CP_DUMP_MAGIC, dpld->ul_map.magic);
- init_completion(&dpld->dump_start_complete);
+ init_completion(&dpld->crash_cmpl);
return 0;
}
-static void _cmc221_idpram_wait_dump(unsigned long arg)
+static void cmc221_idpram_wait_dump(unsigned long arg)
{
struct dpram_link_device *dpld = (struct dpram_link_device *)arg;
u16 msg;
- msg = dpld->dpctl->recv_msg();
+ msg = cmc221_idpram_recv_msg(dpld);
if (msg == CMC22x_CP_DUMP_END) {
- complete_all(&dpld->dump_recv_done);
+ complete_all(&dpld->crash_cmpl);
return;
}
- if (((dpld->dump_rcvd & 0x1) == 0) && (msg == CMC22x_1ST_BUFF_FULL)) {
- complete_all(&dpld->dump_recv_done);
+ if (((dpld->crash_rcvd & 0x1) == 0) && (msg == CMC22x_1ST_BUFF_FULL)) {
+ complete_all(&dpld->crash_cmpl);
return;
}
- if (((dpld->dump_rcvd & 0x1) == 1) && (msg == CMC22x_2ND_BUFF_FULL)) {
- complete_all(&dpld->dump_recv_done);
+ if (((dpld->crash_rcvd & 0x1) == 1) && (msg == CMC22x_2ND_BUFF_FULL)) {
+ complete_all(&dpld->crash_cmpl);
return;
}
- mif_add_timer(&dpld->dump_timer, DUMP_WAIT_TIMEOUT,
- _cmc221_idpram_wait_dump, (unsigned long)dpld);
+ mif_add_timer(&dpld->crash_timer, DUMP_WAIT_TIMEOUT,
+ cmc221_idpram_wait_dump, (unsigned long)dpld);
}
-static int _cmc221_idpram_upload(struct dpram_link_device *dpld,
- struct dpram_dump_arg *dumparg)
+static int cmc221_idpram_upload(struct dpram_link_device *dpld,
+ struct dpram_dump_arg *dumparg)
{
- struct link_device *ld = &dpld->ld;
int ret;
u8 __iomem *src;
int buff_size = CMC22x_DUMP_BUFF_SIZE;
- if ((dpld->dump_rcvd & 0x1) == 0)
- dpld->dpctl->send_msg(CMC22x_1ST_BUFF_READY);
+ if ((dpld->crash_rcvd & 0x1) == 0)
+ cmc221_idpram_send_msg(dpld, CMC22x_1ST_BUFF_READY);
else
- dpld->dpctl->send_msg(CMC22x_2ND_BUFF_READY);
+ cmc221_idpram_send_msg(dpld, CMC22x_2ND_BUFF_READY);
- init_completion(&dpld->dump_recv_done);
+ init_completion(&dpld->crash_cmpl);
- mif_add_timer(&dpld->dump_timer, DUMP_WAIT_TIMEOUT,
- _cmc221_idpram_wait_dump, (unsigned long)dpld);
+ mif_add_timer(&dpld->crash_timer, DUMP_WAIT_TIMEOUT,
+ cmc221_idpram_wait_dump, (unsigned long)dpld);
- ret = wait_for_completion_interruptible_timeout(
- &dpld->dump_recv_done, DUMP_TIMEOUT);
+ ret = wait_for_completion_timeout(&dpld->crash_cmpl, DUMP_TIMEOUT);
if (!ret) {
- mif_info("%s: ERR! CP didn't send dump data!!!\n", ld->name);
+ mif_info("ERR! no dump from CP!!!\n");
goto err_out;
}
- if (dpld->dpctl->recv_msg() == CMC22x_CP_DUMP_END) {
- mif_info("%s: CMC22x_CP_DUMP_END\n", ld->name);
+ if (cmc221_idpram_recv_msg(dpld) == CMC22x_CP_DUMP_END) {
+ mif_info("recv CMC22x_CP_DUMP_END\n");
return 0;
}
- if ((dpld->dump_rcvd & 0x1) == 0)
+ if ((dpld->crash_rcvd & 0x1) == 0)
src = dpld->ul_map.buff;
else
src = dpld->ul_map.buff + CMC22x_DUMP_BUFF_SIZE;
@@ -595,52 +652,64 @@ static int _cmc221_idpram_upload(struct dpram_link_device *dpld,
ret = copy_to_user(dumparg->buff, dpld->buff, buff_size);
if (ret < 0) {
- mif_info("%s: ERR! copy_to_user fail\n", ld->name);
+ mif_info("ERR! copy_to_user fail\n");
goto err_out;
}
- dpld->dump_rcvd++;
+ dpld->crash_rcvd++;
return buff_size;
err_out:
return -EIO;
}
-static int cmc221_dump_update(struct dpram_link_device *dpld, void *arg)
+static int cmc221_dump_update(struct dpram_link_device *dpld, unsigned long arg)
{
- struct link_device *ld = &dpld->ld;
struct dpram_dump_arg dump;
int ret;
ret = copy_from_user(&dump, (void __user *)arg, sizeof(dump));
if (ret < 0) {
- mif_info("%s: ERR! copy_from_user fail\n", ld->name);
+ mif_info("ERR! copy_from_user fail\n");
return ret;
}
- return _cmc221_idpram_upload(dpld, &dump);
+ return cmc221_idpram_upload(dpld, &dump);
}
-static int cmc221_ioctl(struct dpram_link_device *dpld, struct io_device *iod,
- unsigned int cmd, unsigned long arg)
+static void cmc221_idpram_clr_int2ap(struct dpram_link_device *dpld)
{
- struct link_device *ld = &dpld->ld;
- int err = 0;
+ iowrite16(0xFFFF, cmc_sfr.clr_int2ap);
+}
- switch (cmd) {
- case IOCTL_DPRAM_SEND_BOOT:
- err = cmc221_download_boot(dpld, (void *)arg);
- if (err < 0)
- mif_info("%s: ERR! download_boot fail\n", ld->name);
- break;
+static int cmc221_idpram_wakeup(struct dpram_link_device *dpld)
+{
+ int cnt = 0;
- default:
- mif_err("%s: ERR! invalid cmd 0x%08X\n", ld->name, cmd);
- err = -EINVAL;
- break;
+ gpio_set_value(dpld->gpio_cp_wakeup, 1);
+
+ while (!gpio_get_value(dpld->gpio_cp_status)) {
+ if (cnt++ > 10) {
+ if (in_irq())
+ mif_err("ERR! gpio_cp_status == 0 in IRQ\n");
+ else
+ mif_err("ERR! gpio_cp_status == 0\n");
+ return -EACCES;
+ }
+
+ mif_info("gpio_cp_status == 0 (cnt %d)\n", cnt);
+ if (in_interrupt())
+ udelay(1000);
+ else
+ usleep_range(1000, 2000);
}
- return err;
+ return 0;
+}
+
+static void cmc221_idpram_sleep(struct dpram_link_device *dpld)
+{
+ gpio_set_value(dpld->gpio_cp_wakeup, 0);
}
#endif
@@ -652,17 +721,44 @@ enum qc_dload_tag {
QC_DLOAD_TAG_MAX
};
+struct qc_dpram_boot_map {
+ u8 __iomem *buff;
+ u16 __iomem *frame_size;
+ u16 __iomem *tag;
+ u16 __iomem *count;
+};
+
+struct qc_dpram_udl_param {
+ unsigned char *addr;
+ unsigned int size;
+ unsigned int count;
+ unsigned int tag;
+};
+
+struct qc_dpram_udl_check {
+ unsigned int total_size;
+ unsigned int rest_size;
+ unsigned int send_size;
+ unsigned int copy_start;
+ unsigned int copy_complete;
+ unsigned int boot_complete;
+};
+
+static struct qc_dpram_boot_map qc_bt_map;
+static struct qc_dpram_udl_param qc_udl_param;
+static struct qc_dpram_udl_check qc_udl_check;
+
static void qc_dload_task(unsigned long data);
static void qc_init_boot_map(struct dpram_link_device *dpld)
{
- struct qc_dpram_boot_map *bt_map = &dpld->qc_bt_map;
- struct modemlink_dpram_control *dpctl = dpld->dpctl;
+ struct qc_dpram_boot_map *qbt_map = &qc_bt_map;
+ struct modemlink_dpram_data *dpram = dpld->dpram;
- bt_map->buff = dpld->dp_base;
- bt_map->frame_size = (u16 *)(dpld->dp_base + dpctl->boot_size_offset);
- bt_map->tag = (u16 *)(dpld->dp_base + dpctl->boot_tag_offset);
- bt_map->count = (u16 *)(dpld->dp_base + dpctl->boot_count_offset);
+ qbt_map->buff = dpld->base;
+ qbt_map->frame_size = (u16 *)(dpld->base + dpram->boot_size_offset);
+ qbt_map->tag = (u16 *)(dpld->base + dpram->boot_tag_offset);
+ qbt_map->count = (u16 *)(dpld->base + dpram->boot_count_offset);
tasklet_init(&dpld->dl_tsk, qc_dload_task, (unsigned long)dpld);
}
@@ -673,15 +769,15 @@ static int qc_prepare_download(struct dpram_link_device *dpld)
int count = 0;
while (1) {
- if (dpld->udl_check.copy_start) {
- dpld->udl_check.copy_start = 0;
+ if (qc_udl_check.copy_start) {
+ qc_udl_check.copy_start = 0;
break;
}
- msleep_interruptible(10);
+ usleep_range(10000, 11000);
count++;
- if (count > 200) {
+ if (count > 300) {
mif_err("ERR! count %d\n", count);
return -1;
}
@@ -690,32 +786,33 @@ static int qc_prepare_download(struct dpram_link_device *dpld)
return retval;
}
-static void _qc_do_download(struct dpram_link_device *dpld,
- struct dpram_udl_param *param)
+static void qc_do_download(struct dpram_link_device *dpld,
+ struct qc_dpram_udl_param *param)
{
- struct qc_dpram_boot_map *bt_map = &dpld->qc_bt_map;
+ struct qc_dpram_boot_map *qbt_map = &qc_bt_map;
- if (param->size <= dpld->dpctl->max_boot_frame_size) {
- memcpy(bt_map->buff, param->addr, param->size);
- iowrite16(param->size, bt_map->frame_size);
- iowrite16(param->tag, bt_map->tag);
- iowrite16(param->count, bt_map->count);
+ if (param->size <= dpld->dpram->max_boot_frame_size) {
+ memcpy(qbt_map->buff, param->addr, param->size);
+ iowrite16(param->size, qbt_map->frame_size);
+ iowrite16(param->tag, qbt_map->tag);
+ iowrite16(param->count, qbt_map->count);
dpld->send_intr(dpld, 0xDB12);
} else {
mif_info("param->size %d\n", param->size);
}
}
-static int _qc_download(struct dpram_link_device *dpld, void *arg,
+static int qc_download(struct dpram_link_device *dpld, void *arg,
enum qc_dload_tag tag)
{
int retval = 0;
int count = 0;
int cnt_limit;
unsigned char *img;
- struct dpram_udl_param param;
+ struct qc_dpram_udl_param param;
- retval = copy_from_user((void *)&param, (void *)arg, sizeof(param));
+ retval = copy_from_user((void *)&param, (void __user *)arg,
+ sizeof(param));
if (retval < 0) {
mif_err("ERR! copy_from_user fail\n");
return -1;
@@ -729,24 +826,24 @@ static int _qc_download(struct dpram_link_device *dpld, void *arg,
memset(img, 0, param.size);
memcpy(img, param.addr, param.size);
- dpld->udl_check.total_size = param.size;
- dpld->udl_check.rest_size = param.size;
- dpld->udl_check.send_size = 0;
- dpld->udl_check.copy_complete = 0;
+ qc_udl_check.total_size = param.size;
+ qc_udl_check.rest_size = param.size;
+ qc_udl_check.send_size = 0;
+ qc_udl_check.copy_complete = 0;
- dpld->udl_param.addr = img;
- dpld->udl_param.size = dpld->dpctl->max_boot_frame_size;
+ qc_udl_param.addr = img;
+ qc_udl_param.size = dpld->dpram->max_boot_frame_size;
if (tag == QC_DLOAD_TAG_NV)
- dpld->udl_param.count = 1;
+ qc_udl_param.count = 1;
else
- dpld->udl_param.count = param.count;
- dpld->udl_param.tag = tag;
+ qc_udl_param.count = param.count;
+ qc_udl_param.tag = tag;
- if (dpld->udl_check.rest_size < dpld->dpctl->max_boot_frame_size)
- dpld->udl_param.size = dpld->udl_check.rest_size;
+ if (qc_udl_check.rest_size < dpld->dpram->max_boot_frame_size)
+ qc_udl_param.size = qc_udl_check.rest_size;
/* Download image (binary or NV) */
- _qc_do_download(dpld, &dpld->udl_param);
+ qc_do_download(dpld, &qc_udl_param);
/* Wait for completion
*/
@@ -756,18 +853,18 @@ static int _qc_download(struct dpram_link_device *dpld, void *arg,
cnt_limit = 1000;
while (1) {
- if (dpld->udl_check.copy_complete) {
- dpld->udl_check.copy_complete = 0;
+ if (qc_udl_check.copy_complete) {
+ qc_udl_check.copy_complete = 0;
retval = 0;
break;
}
- msleep(10);
+ usleep_range(10000, 11000);
count++;
if (count > cnt_limit) {
- dpld->udl_check.total_size = 0;
- dpld->udl_check.rest_size = 0;
+ qc_udl_check.total_size = 0;
+ qc_udl_check.rest_size = 0;
mif_err("ERR! count %d\n", count);
retval = -1;
break;
@@ -781,51 +878,51 @@ static int _qc_download(struct dpram_link_device *dpld, void *arg,
static int qc_download_binary(struct dpram_link_device *dpld, void *arg)
{
- return _qc_download(dpld, arg, QC_DLOAD_TAG_BIN);
+ return qc_download(dpld, arg, QC_DLOAD_TAG_BIN);
}
static int qc_download_nv(struct dpram_link_device *dpld, void *arg)
{
- return _qc_download(dpld, arg, QC_DLOAD_TAG_NV);
+ return qc_download(dpld, arg, QC_DLOAD_TAG_NV);
}
static void qc_dload_task(unsigned long data)
{
struct dpram_link_device *dpld = (struct dpram_link_device *)data;
- dpld->udl_check.send_size += dpld->udl_param.size;
- dpld->udl_check.rest_size -= dpld->udl_param.size;
+ qc_udl_check.send_size += qc_udl_param.size;
+ qc_udl_check.rest_size -= qc_udl_param.size;
- dpld->udl_param.addr += dpld->udl_param.size;
+ qc_udl_param.addr += qc_udl_param.size;
- if (dpld->udl_check.send_size >= dpld->udl_check.total_size) {
- dpld->udl_check.copy_complete = 1;
- dpld->udl_param.tag = 0;
+ if (qc_udl_check.send_size >= qc_udl_check.total_size) {
+ qc_udl_check.copy_complete = 1;
+ qc_udl_param.tag = 0;
return;
}
- if (dpld->udl_check.rest_size < dpld->dpctl->max_boot_frame_size)
- dpld->udl_param.size = dpld->udl_check.rest_size;
+ if (qc_udl_check.rest_size < dpld->dpram->max_boot_frame_size)
+ qc_udl_param.size = qc_udl_check.rest_size;
- dpld->udl_param.count += 1;
+ qc_udl_param.count += 1;
- _qc_do_download(dpld, &dpld->udl_param);
+ qc_do_download(dpld, &qc_udl_param);
}
static void qc_dload_cmd_handler(struct dpram_link_device *dpld, u16 cmd)
{
switch (cmd) {
case 0x1234:
- dpld->udl_check.copy_start = 1;
+ qc_udl_check.copy_start = 1;
break;
case 0xDBAB:
- if (dpld->udl_check.total_size)
+ if (qc_udl_check.total_size)
tasklet_schedule(&dpld->dl_tsk);
break;
case 0xABCD:
- dpld->udl_check.boot_complete = 1;
+ qc_udl_check.boot_complete = 1;
break;
default:
@@ -843,12 +940,12 @@ static int qc_boot_start(struct dpram_link_device *dpld)
dpld->send_intr(dpld, mask);
while (1) {
- if (dpld->udl_check.boot_complete) {
- dpld->udl_check.boot_complete = 0;
+ if (qc_udl_check.boot_complete) {
+ qc_udl_check.boot_complete = 0;
break;
}
- msleep_interruptible(10);
+ usleep_range(10000, 11000);
count++;
if (count > 200) {
@@ -870,7 +967,7 @@ static int qc_boot_post_process(struct dpram_link_device *dpld)
break;
}
- msleep_interruptible(10);
+ usleep_range(10000, 11000);
count++;
if (count > 200) {
@@ -900,27 +997,26 @@ static void qc_start_handler(struct dpram_link_device *dpld)
static void qc_crash_log(struct dpram_link_device *dpld)
{
- struct link_device *ld = &dpld->ld;
static unsigned char buf[151];
u8 __iomem *data = NULL;
- data = dpld->get_rx_buff(dpld, IPC_FMT);
+ data = dpld->get_rxq_buff(dpld, IPC_FMT);
memcpy(buf, data, (sizeof(buf) - 1));
- mif_info("PHONE ERR MSG\t| %s Crash\n", ld->mdm_data->name);
+ mif_info("PHONE ERR MSG\t| %s Crash\n", dpld->ld.mc->name);
mif_info("PHONE ERR MSG\t| %s\n", buf);
}
-static int _qc_data_upload(struct dpram_link_device *dpld,
- struct dpram_udl_param *param)
+static int qc_data_upload(struct dpram_link_device *dpld,
+ struct qc_dpram_udl_param *param)
{
- struct qc_dpram_boot_map *bt_map = &dpld->qc_bt_map;
+ struct qc_dpram_boot_map *qbt_map = &qc_bt_map;
int retval = 0;
u16 intval = 0;
int count = 0;
while (1) {
- if (!gpio_get_value(dpld->gpio_dpram_int)) {
+ if (!gpio_get_value(dpld->gpio_int2ap)) {
intval = dpld->recv_intr(dpld);
if (intval == 0xDBAB) {
break;
@@ -930,7 +1026,7 @@ static int _qc_data_upload(struct dpram_link_device *dpld,
}
}
- msleep_interruptible(1);
+ usleep_range(1000, 2000);
count++;
if (count > 200) {
@@ -939,10 +1035,10 @@ static int _qc_data_upload(struct dpram_link_device *dpld,
}
}
- param->size = ioread16(bt_map->frame_size);
- memcpy(param->addr, bt_map->buff, param->size);
- param->tag = ioread16(bt_map->tag);
- param->count = ioread16(bt_map->count);
+ param->size = ioread16(qbt_map->frame_size);
+ memcpy(param->addr, qbt_map->buff, param->size);
+ param->tag = ioread16(qbt_map->tag);
+ param->count = ioread16(qbt_map->count);
dpld->send_intr(dpld, 0xDB12);
@@ -961,7 +1057,7 @@ static int qc_uload_step1(struct dpram_link_device *dpld)
mif_info("+---------------------------------------------+\n");
while (1) {
- if (!gpio_get_value(dpld->gpio_dpram_int)) {
+ if (!gpio_get_value(dpld->gpio_int2ap)) {
intval = dpld->recv_intr(dpld);
mif_info("intr 0x%04x\n", intval);
if (intval == 0x1234) {
@@ -972,7 +1068,7 @@ static int qc_uload_step1(struct dpram_link_device *dpld)
}
}
- msleep_interruptible(1);
+ usleep_range(1000, 2000);
count++;
if (count > 200) {
@@ -993,17 +1089,18 @@ static int qc_uload_step1(struct dpram_link_device *dpld)
static int qc_uload_step2(struct dpram_link_device *dpld, void *arg)
{
int retval = 0;
- struct dpram_udl_param param;
+ struct qc_dpram_udl_param param;
- retval = copy_from_user((void *)&param, (void *)arg, sizeof(param));
+ retval = copy_from_user((void *)&param, (void __user *)arg,
+ sizeof(param));
if (retval < 0) {
mif_err("ERR! copy_from_user fail (err %d)\n", retval);
return -1;
}
- retval = _qc_data_upload(dpld, &param);
+ retval = qc_data_upload(dpld, &param);
if (retval < 0) {
- mif_err("ERR! _qc_data_upload fail (err %d)\n", retval);
+ mif_err("ERR! qc_data_upload fail (err %d)\n", retval);
return -1;
}
@@ -1025,40 +1122,39 @@ static int qc_uload_step2(struct dpram_link_device *dpld, void *arg)
}
static int qc_ioctl(struct dpram_link_device *dpld, struct io_device *iod,
- unsigned int cmd, unsigned long arg)
+ unsigned int cmd, unsigned long arg)
{
- struct link_device *ld = &dpld->ld;
int err = 0;
switch (cmd) {
case IOCTL_DPRAM_PHONE_POWON:
err = qc_prepare_download(dpld);
if (err < 0)
- mif_info("%s: ERR! prepare_download fail\n", ld->name);
+ mif_info("ERR! prepare_download fail\n");
break;
case IOCTL_DPRAM_PHONEIMG_LOAD:
err = qc_download_binary(dpld, (void *)arg);
if (err < 0)
- mif_info("%s: ERR! download_binary fail\n", ld->name);
+ mif_info("ERR! download_binary fail\n");
break;
case IOCTL_DPRAM_NVDATA_LOAD:
err = qc_download_nv(dpld, (void *)arg);
if (err < 0)
- mif_info("%s: ERR! download_nv fail\n", ld->name);
+ mif_info("ERR! download_nv fail\n");
break;
case IOCTL_DPRAM_PHONE_BOOTSTART:
err = qc_boot_start(dpld);
if (err < 0) {
- mif_info("%s: ERR! boot_start fail\n", ld->name);
+ mif_info("ERR! boot_start fail\n");
break;
}
err = qc_boot_post_process(dpld);
if (err < 0)
- mif_info("%s: ERR! boot_post_process fail\n", ld->name);
+ mif_info("ERR! boot_post_process fail\n");
break;
@@ -1067,7 +1163,7 @@ static int qc_ioctl(struct dpram_link_device *dpld, struct io_device *iod,
err = qc_uload_step1(dpld);
if (err < 0) {
enable_irq(dpld->irq);
- mif_info("%s: ERR! upload_step1 fail\n", ld->name);
+ mif_info("ERR! upload_step1 fail\n");
}
break;
@@ -1075,12 +1171,12 @@ static int qc_ioctl(struct dpram_link_device *dpld, struct io_device *iod,
err = qc_uload_step2(dpld, (void *)arg);
if (err < 0) {
enable_irq(dpld->irq);
- mif_info("%s: ERR! upload_step2 fail\n", ld->name);
+ mif_info("ERR! upload_step2 fail\n");
}
break;
default:
- mif_err("%s: ERR! invalid cmd 0x%08X\n", ld->name, cmd);
+ mif_err("ERR! invalid cmd 0x%08X\n", cmd);
err = -EINVAL;
break;
}
@@ -1091,44 +1187,402 @@ static int qc_ioctl(struct dpram_link_device *dpld, struct io_device *iod,
static irqreturn_t qc_dpram_irq_handler(int irq, void *data)
{
struct dpram_link_device *dpld = (struct dpram_link_device *)data;
- struct link_device *ld = (struct link_device *)&dpld->ld;
+ struct link_device *ld = &dpld->ld;
+ struct mem_status stat;
u16 int2ap = 0;
- if (unlikely(ld->mode == LINK_MODE_OFFLINE))
+ if (ld->mode == LINK_MODE_OFFLINE) {
+ int2ap = dpld->recv_intr(dpld);
return IRQ_HANDLED;
+ }
- int2ap = dpld->recv_intr(dpld);
+ dpld->get_dpram_status(dpld, RX, &stat);
+ int2ap = stat.int2ap;
if (int2ap == INT_POWERSAFE_FAIL) {
- mif_info("%s: int2ap == INT_POWERSAFE_FAIL\n", ld->name);
+ mif_info("int2ap == INT_POWERSAFE_FAIL\n");
goto exit;
}
if (int2ap == 0x1234 || int2ap == 0xDBAB || int2ap == 0xABCD) {
qc_dload_cmd_handler(dpld, int2ap);
goto exit;
+ } else if (int2ap == 0x4321 || int2ap == 0x5432) {
+ mif_err("ERR! CP error command (0x%04X)\n", int2ap);
+ goto exit;
}
if (likely(INT_VALID(int2ap)))
- dpld->ipc_rx_handler(dpld, int2ap);
+ dpld->ipc_rx_handler(dpld, &stat);
else
- mif_info("%s: ERR! invalid intr 0x%04X\n", ld->name, int2ap);
+ mif_info("ERR! invalid intr 0x%04X\n", int2ap);
exit:
return IRQ_HANDLED;
}
#endif
-static struct dpram_ext_op ext_op_set[] = {
+#if defined(CONFIG_CDMA_MODEM_QSC6085)
+#define CMD_CP_RAMDUMP_START_REQ 0x9200
+#define CMD_CP_RAMDUMP_SEND_REQ 0x9400
+#define CMD_CP_RAMDUMP_SEND_DONE_REQ 0x9600
+
+#define CMD_CP_RAMDUMP_START_RESP 0x0300
+#define CMD_CP_RAMDUMP_SEND_RESP 0x0500
+#define CMD_CP_RAMDUMP_SEND_DONE_RESP 0x0700
+
+#define QSC_UPLOAD_MODE (0x444D554C)
+#define QSC_UPLOAD_MODE_COMPLETE (0xABCDEF90)
+
+#define RAMDUMP_CMD_TIMEOUT (5 * HZ)
+#define QSC6085_RAM_SIZE (32 * 1024 * 1024) /* 32MB */
+
+struct qsc6085_dump_command {
+ u32 addr;
+ u32 size;
+ u32 copyto_offset;
+};
+
+struct qsc6085_dump_status {
+ u32 dump_size;
+ u32 addr;
+ u32 rcvd;
+ u32 rest;
+};
+
+static struct qsc6085_dump_status qsc_dump_stat;
+
+static void qsc6085_dump_work(struct work_struct *work);
+
+static void qsc6085_init_dl_map(struct dpram_link_device *dpld)
+{
+ dpld->dl_map.magic = (u32 *)dpld->base;
+ dpld->dl_map.buff = (u8 *)(dpld->base + DP_DLOAD_BUFF_OFFSET);
+}
+
+static void qsc6085_init_ul_map(struct dpram_link_device *dpld)
+{
+ int magic_size = DP_ULOAD_MAGIC_SIZE;
+ int cmd_size = sizeof(struct qsc6085_dump_command);
+ int mbx_size = DP_MBX_SET_SIZE;
+
+ dpld->ul_map.magic = (u32 *)dpld->base;
+ dpld->ul_map.cmd = dpld->base + magic_size;
+ dpld->ul_map.cmd_size = cmd_size;
+ dpld->ul_map.buff = dpld->base + magic_size + cmd_size;
+ dpld->ul_map.space = dpld->size - (magic_size + cmd_size + mbx_size);
+}
+
+static void qsc6085_req_active_handler(struct dpram_link_device *dpld)
+{
+ struct modem_ctl *mc = dpld->ld.mc;
+ mif_info("pda_active = %d\n", gpio_get_value(mc->gpio_pda_active));
+ dpld->send_intr(dpld, INT_CMD(INT_CMD_RES_ACTIVE));
+}
+
+static void qsc6085_error_display_handler(struct dpram_link_device *dpld)
+{
+ struct link_device *ld = &dpld->ld;
+ struct io_device *iod;
+
+ mif_err("recv 0xC9 (CRASH_EXIT)\n");
+ mif_err("CP Crash: %s\n", dpld->get_rxq_buff(dpld, IPC_FMT));
+
+ iod = link_get_iod_with_format(ld, IPC_FMT);
+ if (iod)
+ iod->modem_state_changed(iod, STATE_CRASH_EXIT);
+}
+
+static void qsc6085_start_handler(struct dpram_link_device *dpld)
+{
+ struct link_device *ld = &dpld->ld;
+ struct io_device *iod;
+
+ mif_info("recv 0xC8 (CP_START)\n");
+
+ mif_info("send 0xC1 (INIT_START)\n");
+ dpld->send_intr(dpld, INT_CMD(INT_CMD_INIT_START));
+
+ dpld->reset_dpram_ipc(dpld);
+
+ iod = link_get_iod_with_format(ld, IPC_FMT);
+ if (!iod) {
+ mif_err("ERR! no iod\n");
+ return;
+ }
+ iod->modem_state_changed(iod, STATE_ONLINE);
+
+ mif_info("send 0xC2 (INIT_END)\n");
+ dpld->send_intr(dpld, INT_CMD(INT_CMD_INIT_END));
+}
+
+static void qsc6085_command_handler(struct dpram_link_device *dpld, u16 cmd)
+{
+ switch (INT_CMD_MASK(cmd)) {
+ case INT_CMD_REQ_ACTIVE:
+ qsc6085_req_active_handler(dpld);
+ break;
+
+ case INT_CMD_ERR_DISPLAY:
+#ifdef CONFIG_LINK_DEVICE_S5P_IDPRAM
+ /* If modem crashes while PDA_SLEEP is in progres */
+ dpld->pm_op->halt_suspend(dpld);
+#endif
+ qsc6085_error_display_handler(dpld);
+ break;
+
+ case INT_CMD_PHONE_START:
+ qsc6085_start_handler(dpld);
+ complete_all(&ld->init_cmpl);
+ break;
+
+#ifdef CONFIG_LINK_DEVICE_S5P_IDPRAM
+ case INT_CMD_IDPRAM_SUSPEND_ACK:
+ dpld->pm_op->power_down(dpld);
+ break;
+
+ case INT_CMD_IDPRAM_WAKEUP_START:
+ dpld->pm_op->power_up(dpld);
+ break;
+#endif
+
+ case INT_CMD_NORMAL_POWER_OFF:
+ complete(&dpld->crash_cmpl);
+ qsc6085_error_display_handler(dpld);
+ break;
+
+ default:
+ mif_err("unknown command 0x%04X\n", cmd);
+ break;
+ }
+}
+
+static int qsc6085_download_firmware(struct dpram_link_device *dpld,
+ struct modem_firmware *fw)
+{
+ int ret = 0;
+ char __user *src = fw->binary;
+ int rest = fw->size;
+ char __iomem *dst = NULL;
+ unsigned long timeout;
+ u16 curr_frame = 0;
+ u16 len = 0;
+ struct dpram_udl_header header;
+
+ header.bop = START_FLAG;
+ header.num_frames = DIV_ROUND_UP(len, DP_DEFAULT_WRITE_LEN);
+ mif_err("FW %d bytes = %d frames\n", fw->size, header.num_frames);
+
+ while (rest > 0) {
+ curr_frame++;
+ len = min(rest, DP_DEFAULT_WRITE_LEN);
+
+ header.curr_frame = curr_frame;
+ header.len = len;
+ mif_info(">>> frame# %u, len %u\n", curr_frame, len);
+
+ dst = dpld->dl_map.buff;
+ memcpy(dst, &header, sizeof(header));
+
+ dst += sizeof(header);
+ ret = copy_from_user(dst, (void __user *)src, len);
+ if (ret < 0) {
+ mif_err("copy_from_user fail\n");
+ return -EIO;
+ }
+
+ dst += len;
+ src += len;
+ rest -= len;
+
+ iowrite8(END_FLAG, (dst+3));
+
+ if (curr_frame == 1) {
+ dpld->send_intr(dpld, 0);
+ timeout = UDL_TIMEOUT;
+ } else {
+ dpld->send_intr(dpld, CMD_DL_SEND_REQ);
+ timeout = UDL_SEND_TIMEOUT;
+ }
+
+ ret = wait_for_completion_timeout(&dpld->udl_cmpl, timeout);
+ if (!ret) {
+ mif_err("ERR! no response from CP\n");
+ return -EIO;
+ }
+ }
+
+ mif_err("send CMD_DL_DONE_REQ to CP\n");
+ dpld->send_intr(dpld, CMD_DL_DONE_REQ);
+
+ ret = wait_for_completion_timeout(&dpld->udl_cmpl, UDL_TIMEOUT);
+ if (!ret) {
+ mif_err("ERR! no response from CP\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int qsc6085_dload_firmware(struct dpram_link_device *dpld,
+ unsigned long arg)
+{
+ int ret;
+ struct modem_firmware fw;
+ mif_err("+++\n");
+
+ ret = copy_from_user(&fw, (void __user *)arg, sizeof(fw));
+ if (ret < 0) {
+ mif_err("ERR! copy_from_user fail!\n");
+ return ret;
+ }
+
+ ret = qsc6085_download_firmware(dpld, &fw);
+
+ mif_err("---\n");
+ return ret;
+}
+
+static int qsc6085_dump_start(struct dpram_link_device *dpld)
+{
+ int ret;
+ struct link_device *ld = &dpld->ld;
+ struct modem_ctl *mc = ld->mc;
+ struct qsc6085_dump_status *dump_stat = &qsc_dump_stat;
+ mif_err("+++\n");
+
+ init_completion(&dpld->crash_cmpl);
+ INIT_DELAYED_WORK(&dpld->crash_dwork, qsc6085_dump_work);
+
+ iowrite32(QSC_UPLOAD_MODE, &dpld->ul_map.magic);
+
+ /* reset modem so that it goes to upload mode */
+ /* ap does not need to reset cp during CRASH_EXIT case */
+ if (gpio_get_value(mc->gpio_phone_active))
+ mc->ops.modem_reset(mc);
+
+ dpld->send_intr(dpld, CMD_CP_RAMDUMP_START_REQ);
+ ret = wait_for_completion_timeout(&dpld->crash_cmpl,
+ RAMDUMP_CMD_TIMEOUT);
+ if (!ret) {
+ mif_err("ERR! no response to CP_RAMDUMP_START_REQ\n");
+ dump_stat->dump_size = 0;
+ } else {
+ dump_stat->dump_size = QSC6085_RAM_SIZE;
+ dump_stat->addr = 0;
+ dump_stat->rcvd = 0;
+ dump_stat->rest = dump_stat->dump_size;
+ }
+
+ queue_delayed_work(system_nrt_wq, &dpld->crash_dwork, 0);
+
+ mif_err("---\n");
+ return 0;
+}
+
+static int qsc6085_dump_update(struct dpram_link_device *dpld,
+ unsigned long arg)
+{
+ int ret;
+ struct link_device *ld = &dpld->ld;
+ struct io_device *iod = link_get_iod_with_format(ld, IPC_RAMDUMP);
+ struct memif_uload_map *ul_map = &dpld->ul_map;
+ struct qsc6085_dump_status *dump_stat = &qsc_dump_stat;
+ struct qsc6085_dump_command dump_cmd;
+
+ while (iod->sk_rx_q.qlen > 0)
+ usleep_range(1000, 1100);
+
+ memset(&dump_cmd, 0, sizeof(dump_cmd));
+ dump_cmd.addr = dump_stat->addr;
+ dump_cmd.size = min(dump_stat->rest, ul_map->space);
+ dump_cmd.copyto_offset = 0x38000010;
+
+ memcpy_toio(ul_map->cmd, &dump_cmd, ul_map->cmd_size);
+
+ dpld->send_intr(dpld, CMD_CP_RAMDUMP_SEND_REQ);
+ ret = wait_for_completion_timeout(&dpld->crash_cmpl,
+ RAMDUMP_CMD_TIMEOUT);
+ if (!ret) {
+ dump_stat->dump_size = 0;
+ mif_err("ERR! no response to CP_RAMDUMP_SEND_REQ\n");
+ ret = -EIO;
+ goto exit;
+ }
+
+ memcpy_fromio(dpld->buff, ul_map->buff, dump_cmd.size);
+
+ ret = iod->recv(iod, ld, dpld->buff, dump_cmd.size);
+ if (ret < 0)
+ goto exit;
+
+ dump_stat->addr += dump_cmd.size;
+ dump_stat->rcvd += dump_cmd.size;
+ dump_stat->rest -= dump_cmd.size;
+ mif_info("rest = %u bytes\n", dump_stat->rest);
+
+ ret = dump_cmd.size;
+
+exit:
+ return ret;
+}
+
+static void qsc6085_dump_work(struct work_struct *work)
+{
+ struct dpram_link_device *dpld;
+ struct link_device *ld;
+ struct qsc6085_dump_status *dump_stat = &qsc_dump_stat;
+ int ret;
+
+ dpld = container_of(work, struct dpram_link_device, crash_dwork.work);
+ ld = &dpld->ld;
+
+ ret = qsc6085_dump_update(dpld, 0);
+ if (ret > 0 && dump_stat->rest > 0)
+ queue_delayed_work(system_nrt_wq, &dpld->crash_dwork, 0);
+}
+
+static int qsc6085_dump_finish(struct dpram_link_device *dpld,
+ unsigned long arg)
+{
+ int ret;
+ struct completion *cmpl = &dpld->crash_cmpl;
+ mif_err("+++\n");
+
+ init_completion(cmpl);
+
+ dpld->send_intr(dpld, CMD_CP_RAMDUMP_SEND_DONE_REQ);
+
+ ret = wait_for_completion_timeout(cmpl, RAMDUMP_CMD_TIMEOUT);
+ if (!ret) {
+ mif_err("ERR! no response to CP_RAMDUMP_SEND_DONE_REQ\n");
+ ret = -EIO;
+ }
+
+ mif_err("---\n");
+ return ret;
+}
+#endif
+
+static struct dpram_ext_op ext_op_set[MAX_MODEM_TYPE] = {
#ifdef CONFIG_CDMA_MODEM_CBP72
[VIA_CBP72] = {
.exist = 1,
- .init_boot_map = cbp72_init_boot_map,
- .init_dl_map = cbp72_init_dl_map,
- .download_binary = cbp72_download_binary,
- .dump_start = cbp72_dump_start,
- .dump_update = cbp72_dump_update,
- .ioctl = cbp72_ioctl,
+ .init_boot_map = cbp_init_boot_map,
+ .init_dl_map = cbp_init_dl_map,
+ .xmit_binary = cbp_xmit_binary,
+ .dump_start = cbp_dump_start,
+ .dump_update = cbp_dump_update,
+ },
+#endif
+#ifdef CONFIG_CDMA_MODEM_CBP82
+ [VIA_CBP82] = {
+ .exist = 1,
+ .init_boot_map = cbp_init_boot_map,
+ .init_dl_map = cbp_init_dl_map,
+ .xmit_binary = cbp_xmit_binary,
+ .dump_start = cbp_dump_start,
+ .dump_update = cbp_dump_update,
},
#endif
#ifdef CONFIG_LTE_MODEM_CMC221
@@ -1137,10 +1591,14 @@ static struct dpram_ext_op ext_op_set[] = {
.init_boot_map = cmc221_init_boot_map,
.init_dl_map = cmc221_init_dl_map,
.init_ul_map = cmc221_init_ul_map,
- .download_binary = cmc221_download_binary,
+ .init_ipc_map = cmc221_init_ipc_map,
+ .xmit_boot = cmc221_xmit_boot,
+ .xmit_binary = cmc221_xmit_binary,
.dump_start = cmc221_dump_start,
.dump_update = cmc221_dump_update,
- .ioctl = cmc221_ioctl,
+ .clear_int2ap = cmc221_idpram_clr_int2ap,
+ .wakeup = cmc221_idpram_wakeup,
+ .sleep = cmc221_idpram_sleep,
},
#endif
#if defined(CONFIG_CDMA_MODEM_MDM6600)
@@ -1163,6 +1621,18 @@ static struct dpram_ext_op ext_op_set[] = {
.irq_handler = qc_dpram_irq_handler,
},
#endif
+#if defined(CONFIG_CDMA_MODEM_QSC6085)
+ [QC_QSC6085] = {
+ .exist = 1,
+ .init_dl_map = qsc6085_init_dl_map,
+ .init_ul_map = qsc6085_init_ul_map,
+ .cmd_handler = qsc6085_command_handler,
+ .firm_update = qsc6085_dload_firmware,
+ .dump_start = qsc6085_dump_start,
+ .dump_update = qsc6085_dump_update,
+ .dump_finish = qsc6085_dump_finish,
+ },
+#endif
};
struct dpram_ext_op *dpram_get_ext_op(enum modem_t modem)
@@ -1173,3 +1643,422 @@ struct dpram_ext_op *dpram_get_ext_op(enum modem_t modem)
return NULL;
}
+#ifdef CONFIG_LINK_DEVICE_S5P_IDPRAM
+#define GPIO_IDPRAM_SFN S3C_GPIO_SFN(2)
+
+#define MAX_CHECK_RETRY_CNT 5
+#define MAX_RESUME_TRY_CNT 5
+
+static bool s5p_idpram_is_pm_locked(struct dpram_link_device *dpld)
+{
+ struct modem_ctl *mc = dpld->ld.mc;
+ struct idpram_pm_data *pm_data = &dpld->pm_data;
+
+ /* If PM is in SUSPEND */
+ if (atomic_read(&pm_data->pm_lock) > 0) {
+ mif_info("in SUSPEND\n");
+ return true;
+ }
+
+ /* If AP is in or into LPA */
+ if (!gpio_get_value(mc->gpio_pda_active)) {
+ mif_info("in LPA\n");
+ return true;
+ }
+
+ return false;
+}
+
+static void s5p_idpram_set_pm_lock(struct dpram_link_device *dpld, int lock)
+{
+ struct idpram_pm_data *pm_data = &dpld->pm_data;
+
+ /* 0 = unlock, 1 = lock */
+ switch (lock) {
+ case 0:
+ if (atomic_read(&pm_data->pm_lock))
+ atomic_set(&pm_data->pm_lock, lock);
+ break;
+
+ case 1:
+ if (!atomic_read(&pm_data->pm_lock))
+ atomic_set(&pm_data->pm_lock, lock);
+ break;
+
+ default:
+ break;
+ }
+}
+
+static void s5p_idpram_try_resume(struct work_struct *work)
+{
+ struct idpram_pm_data *pm_data;
+ struct dpram_link_device *dpld;
+ struct link_device *ld;
+ unsigned long delay;
+ u16 cmd;
+ mif_info("+++\n");
+
+ pm_data = container_of(work, struct idpram_pm_data, resume_dwork.work);
+ dpld = container_of(pm_data, struct dpram_link_device, pm_data);
+ ld = &dpld->ld;
+
+ if (pm_data->last_msg == INT_CMD(INT_CMD_IDPRAM_RESUME_REQ)) {
+ pm_data->last_msg = 0;
+
+ s5p_idpram_set_pm_lock(dpld, 0);
+ wake_unlock(&pm_data->hold_wlock);
+
+ delay = msecs_to_jiffies(10);
+ schedule_delayed_work(&pm_data->tx_dwork, delay);
+
+ mif_info("%s resumed\n", ld->name);
+ goto exit;
+ }
+
+ if (pm_data->resume_try_cnt++ < MAX_RESUME_TRY_CNT) {
+ mif_info("%s not resumed yet\n", ld->name);
+
+ cmd = INT_CMD(INT_CMD_IDPRAM_RESUME_REQ);
+ mif_info("send IDPRAM_RESUME_REQ (0x%X)\n", cmd);
+ dpld->send_intr(dpld, cmd);
+
+ delay = msecs_to_jiffies(200);
+ schedule_delayed_work(&pm_data->resume_dwork, delay);
+ } else {
+ struct io_device *iod;
+ mif_err("ERR! %s resume T-I-M-E-O-U-T\n", ld->name);
+
+ iod = link_get_iod_with_format(ld, IPC_FMT);
+ if (iod)
+ iod->modem_state_changed(iod, STATE_CRASH_EXIT);
+
+ wake_unlock(&pm_data->hold_wlock);
+
+ /* hold wakelock until uevnet sent to rild */
+ wake_lock_timeout(&pm_data->hold_wlock, HZ*7);
+ s5p_idpram_set_pm_lock(dpld, 0);
+ }
+
+exit:
+ mif_info("---\n");
+}
+
+static irqreturn_t s5p_cp_dump_irq_handler(int irq, void *data)
+{
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t s5p_ap_wakeup_irq_handler(int irq, void *data)
+{
+ struct idpram_pm_data *pm_data = data;
+ wake_lock_timeout(&pm_data->ap_wlock, HZ*5);
+ return IRQ_HANDLED;
+}
+
+static void s5p_idpram_power_down(struct dpram_link_device *dpld)
+{
+ struct idpram_pm_data *pm_data = &dpld->pm_data;
+ mif_info("+++\n");
+
+ pm_data->last_msg = INT_CMD(INT_CMD_IDPRAM_SUSPEND_ACK);
+ complete(&pm_data->down_cmpl);
+
+ mif_info("---\n");
+}
+
+static void s5p_idpram_power_up(struct dpram_link_device *dpld)
+{
+ struct idpram_pm_data *pm_data = &dpld->pm_data;
+ mif_info("+++\n");
+
+ pm_data->last_msg = INT_CMD(INT_CMD_IDPRAM_RESUME_REQ);
+ pm_data->pm_state = IDPRAM_PM_ACTIVE;
+
+ mif_info("---\n");
+}
+
+static void s5p_idpram_halt_suspend(struct dpram_link_device *dpld)
+{
+ struct idpram_pm_data *pm_data = &dpld->pm_data;
+ mif_info("+++\n");
+
+ complete(&pm_data->down_cmpl);
+
+ mif_info("---\n");
+}
+
+static int s5p_idpram_prepare_suspend(struct dpram_link_device *dpld)
+{
+ struct link_device *ld = &dpld->ld;
+ struct idpram_pm_data *pm_data = &dpld->pm_data;
+ struct modem_ctl *mc = dpld->ld.mc;
+ struct completion *cmpl;
+ unsigned long timeout;
+ unsigned long rest;
+ int cnt = 0;
+ u16 cmd = INT_CMD(INT_CMD_IDPRAM_SUSPEND_REQ);
+ mif_info("+++\n");
+
+ pm_data->pm_state = IDPRAM_PM_SUSPEND_PREPARE;
+ pm_data->last_msg = 0;
+ s5p_idpram_set_pm_lock(dpld, 1);
+
+ /*
+ * Because, if dpram was powered down, cp dpram random intr was
+ * ocurred. so, fixed by muxing cp dpram intr pin to GPIO output
+ * high,..
+ */
+ gpio_set_value(dpld->gpio_int2cp, 1);
+ s3c_gpio_cfgpin(dpld->gpio_int2cp, S3C_GPIO_OUTPUT);
+
+ /* prevent PDA_ACTIVE status is low */
+ gpio_set_value(mc->gpio_pda_active, 1);
+
+ cmpl = &pm_data->down_cmpl;
+ timeout = IDPRAM_SUSPEND_REQ_TIMEOUT;
+ cnt = 0;
+ do {
+ init_completion(cmpl);
+
+ mif_info("send IDPRAM_SUSPEND_REQ (0x%X)\n", cmd);
+ dpld->send_intr(dpld, cmd);
+
+ rest = wait_for_completion_timeout(cmpl, timeout);
+ if (rest == 0) {
+ cnt++;
+ mif_err("timeout!!! (count = %d)\n", cnt);
+ if (cnt >= 3) {
+ mif_err("ERR! no response from CP\n");
+ break;
+ }
+ }
+ } while (rest == 0);
+
+ switch (pm_data->last_msg) {
+ case INT_CMD(INT_CMD_IDPRAM_SUSPEND_ACK):
+ mif_info("recv IDPRAM_SUSPEND_ACK (0x%X)\n", pm_data->last_msg);
+ pm_data->pm_state = IDPRAM_PM_DPRAM_POWER_DOWN;
+ break;
+
+ default:
+ mif_err("ERR! %s down or not ready!!! (intr 0x%04X)\n",
+ ld->name, dpld->recv_intr(dpld));
+ timeout = msecs_to_jiffies(500);
+ wake_lock_timeout(&pm_data->hold_wlock, timeout);
+ s5p_idpram_set_pm_lock(dpld, 0);
+ break;
+ }
+
+ mif_info("---\n");
+ return 0;
+}
+
+static int s5p_idpram_resume_init(struct dpram_link_device *dpld)
+{
+ struct idpram_pm_data *pm_data = &dpld->pm_data;
+ mif_info("+++\n");
+
+ pm_data->pm_state = IDPRAM_PM_RESUME_START;
+ pm_data->last_msg = 0;
+
+ dpld->reset_dpram_ipc(dpld);
+
+ /* re-initialize internal dpram gpios */
+ s3c_gpio_cfgpin(dpld->gpio_int2cp, GPIO_IDPRAM_SFN);
+
+ mif_info("---\n");
+ return 0;
+}
+
+static int s5p_idpram_start_resume(struct dpram_link_device *dpld)
+{
+ struct idpram_pm_data *pm_data = &dpld->pm_data;
+ struct modem_ctl *mc = dpld->ld.mc;
+ unsigned long delay;
+ mif_info("+++ (pm_state = %d)\n", pm_data->pm_state);
+
+ switch (pm_data->pm_state) {
+ /* schedule_work */
+ case IDPRAM_PM_DPRAM_POWER_DOWN:
+ gpio_set_value(mc->gpio_pda_active, 0);
+ msleep(50);
+
+ s5p_idpram_resume_init(dpld);
+ msleep(50);
+
+ gpio_set_value(mc->gpio_pda_active, 1);
+ msleep(20);
+
+ pm_data->resume_try_cnt = 0;
+ wake_lock(&pm_data->hold_wlock);
+
+ delay = msecs_to_jiffies(20);
+ schedule_delayed_work(&pm_data->resume_dwork, delay);
+ break;
+
+ case IDPRAM_PM_RESUME_START:
+ case IDPRAM_PM_SUSPEND_PREPARE:
+ default:
+ break;
+ }
+
+ mif_info("---\n");
+ return 0;
+}
+
+static int s5p_idpram_notify_pm_event(struct notifier_block *this,
+ unsigned long event, void *v)
+{
+ struct idpram_pm_data *pm_data;
+ struct dpram_link_device *dpld;
+ int err;
+ mif_info("+++ (event 0x%08X)\n", (int)event);
+
+ pm_data = container_of(this, struct idpram_pm_data, pm_noti);
+ dpld = container_of(pm_data, struct dpram_link_device, pm_data);
+
+ switch (event) {
+ case PM_SUSPEND_PREPARE:
+ err = s5p_idpram_prepare_suspend(dpld);
+ break;
+
+ case PM_POST_SUSPEND:
+ err = s5p_idpram_start_resume(dpld);
+ break;
+
+ default:
+ break;
+ }
+
+ mif_info("---\n");
+ return NOTIFY_DONE;
+}
+
+static int s5p_idpram_pm_init(struct dpram_link_device *dpld,
+ struct modem_data *modem, void (*pm_tx_func)(struct work_struct *work))
+{
+ struct idpram_pm_data *pm_data = &dpld->pm_data;
+ int err;
+ unsigned gpio;
+ unsigned irq;
+ mif_info("+++\n");
+
+ atomic_set(&pm_data->pm_lock, 0);
+
+ init_completion(&pm_data->down_cmpl);
+
+ wake_lock_init(&pm_data->ap_wlock, WAKE_LOCK_SUSPEND, "ap_wakeup");
+ wake_lock_init(&pm_data->hold_wlock, WAKE_LOCK_SUSPEND, "dpram_hold");
+
+ INIT_DELAYED_WORK(&pm_data->tx_dwork, pm_tx_func);
+ INIT_DELAYED_WORK(&pm_data->resume_dwork, s5p_idpram_try_resume);
+
+ pm_data->resume_try_cnt = 0;
+
+ /* register PM notifier */
+ pm_data->pm_noti.notifier_call = s5p_idpram_notify_pm_event;
+ register_pm_notifier(&pm_data->pm_noti);
+
+ /*
+ ** Register gpio_ap_wakeup interrupt handler
+ */
+ gpio = modem->gpio_ap_wakeup;
+ irq = gpio_to_irq(gpio);
+ mif_info("gpio_ap_wakeup: GPIO# %d, IRQ# %d\n", gpio, irq);
+
+ err = request_irq(irq, s5p_ap_wakeup_irq_handler, IRQF_TRIGGER_RISING,
+ "idpram_ap_wakeup", (void *)pm_data);
+ if (err) {
+ mif_err("ERR! request_irq(#%d) fail (err %d)\n", irq, err);
+ goto exit;
+ }
+
+ err = enable_irq_wake(irq);
+ if (err) {
+ mif_err("ERR! enable_irq_wake(#%d) fail (err %d)\n", irq, err);
+ free_irq(irq, (void *)pm_data);
+ goto exit;
+ }
+
+ /*
+ ** Register gpio_cp_dump_int interrupt handler for LPA mode
+ */
+ gpio = modem->gpio_cp_dump_int;
+ irq = gpio_to_irq(gpio);
+ mif_info("gpio_cp_dump_int: GPIO# %d, IRQ# %d\n", gpio, irq);
+
+ err = request_irq(irq, s5p_cp_dump_irq_handler, IRQF_TRIGGER_RISING,
+ "idpram_cp_dump", (void *)pm_data);
+ if (err) {
+ mif_err("ERR! request_irq(#%d) fail (err %d)\n", irq, err);
+ free_irq(gpio_to_irq(modem->gpio_ap_wakeup), (void *)pm_data);
+ goto exit;
+ }
+
+ err = enable_irq_wake(irq);
+ if (err) {
+ mif_err("ERR! enable_irq_wake(#%d) fail (err %d)\n", irq, err);
+ free_irq(gpio_to_irq(modem->gpio_cp_dump_int), (void *)pm_data);
+ free_irq(gpio_to_irq(modem->gpio_ap_wakeup), (void *)pm_data);
+ goto exit;
+ }
+
+exit:
+ mif_err("---\n");
+ return err;
+}
+
+static bool s5p_idpram_int2cp_possible(struct dpram_link_device *dpld)
+{
+ struct modem_ctl *mc = dpld->ld.mc;
+ int i;
+ int level;
+
+ for (i = 1; i <= MAX_CHECK_RETRY_CNT; i++) {
+ level = gpio_get_value(dpld->gpio_int2cp);
+ if (level)
+ break;
+
+ /* CP has not yet received previous command. */
+ mif_info("gpio_ipc_int2cp == 0 (count %d)\n", i);
+
+ usleep_range(1000, 1100);
+ }
+
+ for (i = 1; i <= MAX_CHECK_RETRY_CNT; i++) {
+ level = gpio_get_value(mc->gpio_pda_active);
+ if (level)
+ break;
+
+ /* AP is in transition to LPA mode. */
+ mif_info("gpio_pda_active == 0 (count %d)\n", i);
+
+ usleep_range(1000, 1100);
+ }
+
+ return true;
+}
+#endif
+
+static struct idpram_pm_op idpram_pm_op_set[MAX_AP_TYPE] = {
+#ifdef CONFIG_LINK_DEVICE_S5P_IDPRAM
+ [S5P] = {
+ .pm_init = s5p_idpram_pm_init,
+ .power_down = s5p_idpram_power_down,
+ .power_up = s5p_idpram_power_up,
+ .halt_suspend = s5p_idpram_halt_suspend,
+ .locked = s5p_idpram_is_pm_locked,
+ .int2cp_possible = s5p_idpram_int2cp_possible,
+ },
+#endif
+};
+
+struct idpram_pm_op *idpram_get_pm_op(enum ap_type ap)
+{
+ if (idpram_pm_op_set[ap].exist)
+ return &idpram_pm_op_set[ap];
+ else
+ return NULL;
+}
+