aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/misc/modem_if_na/modem_link_device_dpram.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/misc/modem_if_na/modem_link_device_dpram.c')
-rw-r--r--drivers/misc/modem_if_na/modem_link_device_dpram.c1504
1 files changed, 1504 insertions, 0 deletions
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;
+}