static int ss300_dump_reset(struct modem_ctl *mc) { struct io_device *iod = mc->iod; struct link_device *ld = get_current_link(iod); unsigned int gpio_cp_reset = mc->gpio_cp_reset; unsigned long flags; mif_disable_irq(&mc->irq_cp_active); mif_info("%s: %s: +++\n", mc->name, FUNC); print_mc_state(mc); spin_lock_irqsave(&mc->lock, flags); iod->modem_state_changed(iod, STATE_CRASH_EXIT); if (mc->wake_lock && !wake_lock_active(mc->wake_lock)) { wake_lock(mc->wake_lock); mif_err("%s->wake_lock locked\n", mc->name); } if (ld->ready) ld->ready(ld); spin_unlock_irqrestore(&mc->lock, flags); gpio_set_value(gpio_cp_reset, 0); print_mc_state(mc); udelay(200); if (ld->reset) ld->reset(ld); gpio_set_value(gpio_cp_reset, 1); print_mc_state(mc); msleep(300); gpio_set_value(mc->gpio_ap_status, 1); mif_info("%s: %s: ---\n", mc->name, FUNC); return 0; }
static int cmc221_dump_reset(struct modem_ctl *mc) { mif_err("%s\n", mc->name); if (!wake_lock_active(&mc->mc_wake_lock)) wake_lock(&mc->mc_wake_lock); set_sromc_access(true); gpio_set_value(mc->gpio_host_active, 0); gpio_set_value(mc->gpio_cp_reset, 0); udelay(200); gpio_set_value(mc->gpio_cp_reset, 1); msleep(300); return 0; }
static irqreturn_t cp_status_handler(int irq, void *data) { struct mem_link_device *mld = (struct mem_link_device *)data; struct link_device *ld = &mld->link_dev; int cp_status = gpio_get_value(mld->gpio_cp_status); unsigned long flags; spin_lock_irqsave(&mld->pm_lock, flags); s5p_change_irq_type(irq, cp_status); if (!cp_online(ld->mc)) goto exit; print_pm_status(mld); if (cp_status) { if (!wake_lock_active(&mld->cp_wlock)) wake_lock(&mld->cp_wlock); } else { if (atomic_read(&mld->ref_cnt) > 0) { /* ** This status means that IPC TX is in progress from AP ** to CP. So, CP_WAKEUP must be set to 1. Otherwise, it ** is a critically erroneous status. */ if (gpio_get_value(mld->gpio_cp_wakeup) == 0) { mif_err("%s: ERR! cp_wakeup == 0\n", ld->name); goto exit; } /* CP_STATUS will be reset to 1 soon due to CP_WAKEUP.*/ } else { gpio_set_value(mld->gpio_ap_status, 0); if (wake_lock_active(&mld->cp_wlock)) wake_unlock(&mld->cp_wlock); } } exit: spin_unlock_irqrestore(&mld->pm_lock, flags); return IRQ_HANDLED; }
int mem_register_ipc_rgn(struct mem_link_device *mld, phys_addr_t start, size_t size) { struct link_device *ld = &mld->link_dev; unsigned int num_pages = (size >> PAGE_SHIFT); struct page **pages; pages = kmalloc(sizeof(struct page *) * num_pages, GFP_ATOMIC); if (!pages) return -ENOMEM; mif_err("%s: IPC_RGN start:%pa size:%zu\n", ld->name, &start, size); mld->start = start; mld->size = size; mld->pages = pages; return 0; }
static void usb_tx_complete(struct urb *urb) { int ret = 0; struct sk_buff *skb = urb->context; switch (urb->status) { case 0: break; default: mif_err("TX error (%d)\n", urb->status); } usb_mark_last_busy(urb->dev); ret = pm_runtime_put_autosuspend(&urb->dev->dev); if (ret < 0 && ret != -EAGAIN) mif_debug("pm_runtime_put_autosuspend failed: %d\n", ret); usb_free_urb(urb); dev_kfree_skb_any(skb); }
static int cbp72_boot_on(struct modem_ctl *mc) { mif_info("\n"); if (!mc->gpio_cp_reset) { mif_err("no gpio data\n"); return -ENXIO; } gpio_set_value(mc->gpio_cp_reset, 0); msleep(600); gpio_set_value(mc->gpio_cp_reset, 1); mc->bootd->modem_state_changed(mc->bootd, STATE_BOOTING); return 0; }
/* TX dynamic switching between DPRAM and USB in one modem */ static irqreturn_t dynamic_switching_handler(int irq, void *arg) { struct modem_ctl *mc = (struct modem_ctl *)arg; int txpath = gpio_get_value(mc->gpio_link_switch); bool enumerated = usb_is_enumerated(mc->msd); mif_err("txpath=%d, enumeration=%d\n", txpath, enumerated); /* do not switch to USB, when USB is not enumerated. */ if (!enumerated && txpath) { mc->need_switch_to_usb = true; return IRQ_HANDLED; } mc->need_switch_to_usb = false; rawdevs_set_tx_link(mc->msd, txpath ? LINKDEV_USB : LINKDEV_DPRAM); return IRQ_HANDLED; }
void mif_print_dump(const char *data, int len, int width) { char *buff; buff = kzalloc(len << 3, GFP_ATOMIC); if (!buff) { mif_err("ERR! kzalloc fail\n"); return; } if (width == 16) mif_dump2format16(data, len, buff, LOG_TAG); else mif_dump2format4(data, len, buff, LOG_TAG); pr_info("%s", buff); kfree(buff); }
static int xmm6262_on(struct modem_ctl *mc) { mif_info("\n"); if (!mc->gpio_cp_reset || !mc->gpio_cp_on || !mc->gpio_reset_req_n) { mif_err("no gpio data\n"); return -ENXIO; } if (mc->gpio_revers_bias_clear) mc->gpio_revers_bias_clear(); #ifdef CONFIG_SEC_DUAL_MODEM_MODE gpio_set_value(mc->gpio_sim_io_sel, 0); gpio_set_value(mc->gpio_cp_ctrl1, 1); gpio_set_value(mc->gpio_cp_ctrl2, 0); #endif /* TODO */ gpio_set_value(mc->gpio_reset_req_n, 0); gpio_set_value(mc->gpio_cp_on, 0); gpio_set_value(mc->gpio_cp_reset, 0); msleep(100); gpio_set_value(mc->gpio_cp_reset, 1); /* If XMM6262 was connected with C2C, AP wait 50ms to BB Reset*/ msleep(50); gpio_set_value(mc->gpio_reset_req_n, 1); gpio_set_value(mc->gpio_cp_on, 1); udelay(60); gpio_set_value(mc->gpio_cp_on, 0); msleep(20); mc->phone_state = STATE_BOOTING; if (mc->gpio_revers_bias_restore) mc->gpio_revers_bias_restore(); gpio_set_value(mc->gpio_pda_active, 1); return 0; }
static void link_pm_runtime_start(struct work_struct *work) { struct link_pm_data *pm_data = container_of(work, struct link_pm_data, link_pm_start.work); struct usb_device *usbdev = pm_data->usb_ld->usbdev; struct device *dev, *hdev; struct link_device *ld = &pm_data->usb_ld->ld; if (!pm_data->usb_ld->if_usb_connected || pm_data->usb_ld->ld.com_state == COM_NONE) { mif_err("disconnect status, ignore\n"); return; } dev = &pm_data->usb_ld->usbdev->dev; /* wait interface driver resumming */ if (dev->power.runtime_status == RPM_SUSPENDED) { mif_info("suspended yet, delayed work\n"); queue_delayed_work(pm_data->wq, &pm_data->link_pm_start, msecs_to_jiffies(20)); return; } if (pm_data->usb_ld->usbdev && dev->parent) { mif_info("rpm_status: %d\n", dev->power.runtime_status); pm_runtime_set_autosuspend_delay(dev, 200); hdev = usbdev->bus->root_hub->dev.parent; mif_info("EHCI runtime %s, %s\n", dev_driver_string(hdev), dev_name(hdev)); pm_runtime_allow(dev); pm_runtime_allow(hdev);/*ehci*/ pm_data->link_pm_active = true; pm_data->resume_requested = false; pm_data->link_reconnect_cnt = 5; pm_data->resume_retry_cnt = 0; /* retry prvious link tx q */ queue_delayed_work(ld->tx_wq, &ld->tx_delayed_work, 0); } }
static void dpram_ipc_write(struct dpram_link_device *dpld, int dev, u32 qsize, u32 in, u32 out, struct sk_buff *skb) { struct link_device *ld = &dpld->ld; u8 __iomem *buff = get_tx_buff(dpld, dev); u8 *src = skb->data; u32 len = skb->len; u32 inp; struct mif_irq_map map; if (in < out) { /* +++++++++ in ---------- out ++++++++++ */ memcpy((buff + in), src, len); } else { /* ------ out +++++++++++ in ------------ */ u32 space = qsize - in; /* 1) in -> buffer end */ memcpy((buff + in), src, ((len > space) ? space : len)); /* 2) buffer start -> out */ if (len > space) memcpy(buff, (src + space), (len - space)); } /* update new in pointer */ inp = in + len; if (inp >= qsize) inp -= qsize; set_tx_head(dpld, dev, inp); if (dev == IPC_FMT) { set_dpram_map(dpld, &map); mif_irq_log(ld->mc->msd, map, "ipc_write", sizeof("ipc_write")); mif_ipc_log(MIF_IPC_AP2CP, ld->mc->msd, skb->data, skb->len); } if (ld->aligned && memcmp16_to_io((buff + in), src, 4)) { mif_err("%s: memcmp16_to_io fail\n", ld->name); dpram_trigger_force_cp_crash(dpld); } }
static void cmd_crash_exit_handler(struct dpram_link_device *dpld) { struct link_device *ld = &dpld->ld; ld->mode = LINK_MODE_ULOAD; if (!wake_lock_active(&dpld->wlock)) wake_lock(&dpld->wlock); mif_err("%s: Recv 0xC9 (CRASH_EXIT)\n", ld->name); dpram_wake_up(dpld); del_timer(&dpld->crash_ack_timer); if (dpld->ext_op && dpld->ext_op->crash_log) dpld->ext_op->crash_log(dpld); handle_cp_crash(dpld); }
bool link_pm_is_connected(struct usb_link_device *usb_ld) { if (has_hub(usb_ld)) { if (usb_ld->link_pm_data->hub_init_lock) return false; if (usb_ld->link_pm_data->hub_status != HUB_STATE_ACTIVE) { schedule_delayed_work( &usb_ld->link_pm_data->link_pm_hub, 0); return false; } } if (!usb_ld->if_usb_connected) { mif_err("mif: if not connected\n"); return false; } return true; }
static inline void stop_pm_wdog(struct modem_link_pm *pm, enum pm_state state, enum pm_event event) { struct pm_wdog *wdog = &pm->wdog; if (state == wdog->state) { struct timer_list *timer = &wdog->timer; if (timer_pending(timer)) del_timer(timer); mif_debug("%s: PM WDOG kicked by {%s@%s}\n", pm->link_name, pm_event2str(event), pm_state2str(state)); } else { mif_err("%s: ERR! PM WDOG illegal state {%s@%s}\n", pm->link_name, pm_event2str(event), pm_state2str(state)); } }
static int cmc221_ioctl(struct dpram_link_device *dpld, struct io_device *iod, unsigned int cmd, unsigned long arg) { int err = 0; switch (cmd) { case IOCTL_DPRAM_SEND_BOOT: err = cmc221_xmit_boot(dpld, arg); if (err < 0) mif_info("ERR! xmit_boot fail\n"); break; default: mif_err("ERR! invalid cmd 0x%08X\n", cmd); err = -EINVAL; break; } return err; }
static void qc_dload_cmd_handler(struct dpram_link_device *dpld, u16 cmd) { switch (cmd) { case 0x1234: dpld->udl_check.copy_start = 1; break; case 0xDBAB: if (dpld->udl_check.total_size) tasklet_schedule(&dpld->dl_tsk); break; case 0xABCD: dpld->udl_check.boot_complete = 1; break; default: mif_err("ERR! unknown command 0x%04X\n", cmd); } }
/** @brief function for the @b dload_start method in a link_device instance Set all flags and environments for CP binary download @param ld the pointer to a link_device instance @param iod the pointer to an io_device instance */ static int mem_start_download(struct link_device *ld, struct io_device *iod) { struct mem_link_device *mld = to_mem_link_device(ld); unsigned int magic; atomic_set(&mld->cp_boot_done, 0); reset_ipc_map(mld); reset_ipc_devices(mld); set_magic(mld, SHM_BOOT_MAGIC); magic = get_magic(mld); if (magic != SHM_BOOT_MAGIC) { mif_err("%s: ERR! magic 0x%08X != SHM_BOOT_MAGIC 0x%08X\n", ld->name, magic, SHM_BOOT_MAGIC); return -EFAULT; } return 0; }
static void pm_wdog_bark(unsigned long data) { struct pm_wdog *wdog = (struct pm_wdog *)data; struct modem_link_pm *pm = wdog_to_pm(wdog); struct pm_fsm *fsm = &pm->fsm; enum pm_state c_state; unsigned long flags; spin_lock_irqsave(&pm->lock, flags); c_state = fsm->state; spin_unlock_irqrestore(&pm->lock, flags); if (wdog->w_state == c_state) { mif_err("%s: PM WDOG lost event {%s@%s}\n", pm->link_name, pm_event2str(wdog->w_event), pm_state2str(wdog->state)); return; } run_pm_fsm(pm, PM_EVENT_WDOG_TIMEOUT); }
/** @brief check the free space in a SBD RB @param rb the pointer to an SBD RB instance @retval "> 0" the size of free space in the @b @@dev TXQ @retval "< 0" an error code */ static inline int check_rb_space(struct sbd_ring_buffer *rb, unsigned int qlen, unsigned int in, unsigned int out) { unsigned int space; if (!circ_valid(qlen, in, out)) { mif_err("ERR! TXQ[%d:%d] DIRTY (qlen:%d in:%d out:%d)\n", rb->id, rb->ch, qlen, in, out); return -EIO; } space = circ_get_space(qlen, in, out); if (unlikely(space < 1)) { mif_err_limited("TXQ[%d:%d] NOSPC (qlen:%d in:%d out:%d)\n", rb->id, rb->ch, qlen, in, out); return -ENOSPC; } return space; }
static int cmc220_off(struct modem_ctl *mc) { mif_info("off\n"); mif_err("<%s>\n", mc->bootd->name); if (!mc->gpio_cp_off || !mc->gpio_cp_on || !mc->gpio_cp_reset) return 0; gpio_set_value(mc->gpio_cp_on, 0); gpio_set_value(mc->gpio_cp_reset, 0); mdelay(300); gpio_set_value(mc->gpio_cp_off, 0); mdelay(300); mc->phone_state = STATE_OFFLINE; return 0; }
static void qc_dload_cmd_handler(struct pld_link_device *pld, u16 cmd) { switch (cmd) { case 0x1234: pld->udl_check.copy_start = 1; break; case 0xDBAB: tasklet_schedule(&pld->dl_tsk); break; case 0xABCD: mif_info("[%s] booting Start\n", pld->ld.name); pld->udl_check.boot_complete = 1; break; default: mif_err("ERR! unknown command 0x%04X\n", cmd); } }
int sbd_pio_tx(struct sbd_ring_buffer *rb, struct sk_buff *skb) { int ret; unsigned int qlen = rb->len; unsigned int in = *rb->wp; unsigned int out = *rb->rp; unsigned int count = skb->len; unsigned int space = (rb->buff_size - rb->payload_offset); u8 *dst; pktlog_tx_bottom_skb(rb->sl, skb); ret = check_rb_space(rb, qlen, in, out); if (unlikely(ret < 0)) return ret; if (unlikely(count > space)) { mif_err("ERR! {id:%d ch:%d} count %d > space %d\n", rb->id, rb->ch, count, space); return -ENOSPC; } barrier(); dst = rb->buff[in] + rb->payload_offset; barrier(); skb_copy_from_linear_data(skb, dst, count); rb->size_v[in] = skb->len; barrier(); *rb->wp = circ_new_ptr(qlen, in, 1); /* Commit the item before incrementing the head */ smp_mb(); return count; }
static int qc_boot_post_process(struct dpram_link_device *dpld) { int count = 0; while (1) { if (dpld->boot_start_complete) { dpld->boot_start_complete = 0; break; } msleep_interruptible(10); count++; if (count > 200) { mif_err("ERR! count %d\n", count); return -1; } } return 0; }
static int cmc221_ioctl(struct dpram_link_device *dpld, struct io_device *iod, unsigned int cmd, unsigned long arg) { struct link_device *ld = &dpld->ld; int err = 0; switch (cmd) { case IOCTL_DPRAM_SEND_BOOT: err = cmc221_download_boot(dpld, (void *)arg); if (err < 0) mif_info("%s: ERR! download_boot fail\n", ld->name); break; default: mif_err("%s: ERR! invalid cmd 0x%08X\n", ld->name, cmd); err = -EINVAL; break; } return err; }
void xmm6262_start_loopback(struct io_device *iod, struct modem_shared *msd) { struct link_device *ld = get_current_link(iod); struct sk_buff *skb = alloc_skb(16, GFP_ATOMIC); int ret; if (unlikely(!skb)) return; memcpy(skb_put(skb, 1), (msd->loopback_ipaddr) ? "s" : "x", 1); skbpriv(skb)->iod = iod; skbpriv(skb)->ld = ld; ret = ld->send(ld, iod, skb); if (ret < 0) { mif_err("usb_tx_urb fail\n"); dev_kfree_skb_any(skb); } mif_info("Send loopback key '%s'\n", (msd->loopback_ipaddr) ? "s" : "x"); }
static int dt_gpio_config(struct modem_ctl *mc, struct modem_data *pdata) { int ret = 0; struct device_node *np = mc->dev->of_node; /* GPIO_RESET_REQ_N */ pdata->gpio_reset_req_n = of_get_named_gpio(np, "mif,gpio_reset_req_n", 0); if (!gpio_is_valid(pdata->gpio_reset_req_n)) { mif_err("reset_req_n: Invalied gpio pins\n"); return -EINVAL; } ret = gpio_request(pdata->gpio_reset_req_n, "RESET_REQ_N"); if (ret) mif_err("fail to request gpio %s:%d\n", "RESET_REQ_N", ret); gpio_direction_output(pdata->gpio_reset_req_n, 0); /* GPIO_CP_DUMP_INT */ pdata->gpio_cp_dump_int = of_get_named_gpio(np, "mif,gpio_cp_dump_int", 0); if (!gpio_is_valid(pdata->gpio_cp_dump_int)) { mif_err("cp_dump_int: Invalied gpio pins\n"); return -EINVAL; } ret = gpio_request(pdata->gpio_cp_dump_int, "CP_DUMP_INT"); if (ret) mif_err("fail to request gpio %s:%d\n", "CP_DUMP_INT", ret); gpio_direction_input(pdata->gpio_cp_dump_int); /* GPIO_AP_DUMP_INT */ pdata->gpio_ap_dump_int = of_get_named_gpio(np, "mif,gpio_ap_dump_int", 0); if (!gpio_is_valid(pdata->gpio_ap_dump_int)) { mif_err("ap_dump_int: Invalied gpio pins\n"); return -EINVAL; } ret = gpio_request(pdata->gpio_ap_dump_int, "AP_DUMP_INT"); if (ret) mif_err("fail to request gpio %s:%d\n", "AP_DUMP_INT", ret); gpio_direction_output(pdata->gpio_ap_dump_int, 0); return ret; }
bool link_pm_is_connected(struct usb_link_device *usb_ld) { if (has_hub(usb_ld)) { struct link_pm_data *pm_data = usb_ld->link_pm_data; if (pm_data->hub_init_lock) return false; if (pm_data->hub_status == HUB_STATE_OFF) { if (pm_data->hub_work_running == false) start_hub_work(pm_data, 0); return false; } } if (!usb_ld->if_usb_connected) { mif_err("mif: if not connected\n"); return false; } return true; }
static int qc_boot_post_process(struct pld_link_device *pld) { int count = 0; while (1) { if (pld->boot_start_complete) { pld->boot_start_complete = 0; break; } usleep_range(10000, 11000); count++; if (count > 200) { mif_err("ERR! count %d\n", count); return -1; } } return 0; }
static void handle_no_response_cp_crash(unsigned long arg) { struct modem_ctl *mc = (struct modem_ctl *)arg; unsigned long flags; if (cp_crashed(mc)) { mif_info("%s: STATE_CRASH_EXIT without response from CP\n", mc->name); return; } mif_err("%s: ERR! No response from CP\n", mc->name); spin_lock_irqsave(&mc->lock, flags); /* Change the modem state for RIL */ if (mc->iod) mc->iod->modem_state_changed(mc->iod, STATE_CRASH_EXIT); /* Change the modem state for CBD */ if (mc->bootd) mc->bootd->modem_state_changed(mc->bootd, STATE_CRASH_EXIT); spin_unlock_irqrestore(&mc->lock, flags); }
static int cbp82_boot_off(struct modem_ctl *mc) { struct link_device *ld = get_current_link(mc->bootd); int ret; mif_err("+++\n"); /* Wait here until the PHONE is up. * Waiting as the this called from IOCTL->UM thread */ mif_err("Waiting for PHONE_START\n"); ret = wait_for_completion_timeout(&ld->init_cmpl, DPRAM_INIT_TIMEOUT); if (!ret) { /* ret == 0 on timeout */ mif_err("T-I-M-E-O-U-T (PHONE_START)\n"); cbp82_off(mc); ret = -EIO; goto exit; } mif_err("recv PHONE_START\n"); mif_err("Waiting for PIF_INIT_DONE\n"); ret = wait_for_completion_timeout(&ld->pif_cmpl, PIF_TIMEOUT); if (!ret) { /* ret == 0 on timeout */ mif_err("T-I-M-E-O-U-T (PIF_INIT_DONE)!!!\n"); cbp82_off(mc); ret = -EIO; goto exit; } mif_err("recv PIF_INIT_DONE\n"); mc->bootd->modem_state_changed(mc->bootd, STATE_ONLINE); ret = 0; exit: wake_unlock(&mc->mc_wake_lock); mif_err("---\n"); return ret; }