static void link_pm_reconnect_work(struct work_struct *work) { struct link_pm_data *pm_data = container_of(work, struct link_pm_data, link_reconnect_work.work); struct modem_ctl *mc = if_usb_get_modemctl(pm_data); if (!mc || pm_data->usb_ld->if_usb_connected) return; if (pm_data->usb_ld->ld.com_state != COM_ONLINE) return; if (pm_data->link_reconnect_cnt--) { if (mc->phone_state == STATE_ONLINE && !pm_data->link_reconnect()) /* try reconnect and check */ schedule_delayed_work(&pm_data->link_reconnect_work, msecs_to_jiffies(500)); else /* under cp crash or reset, just return */ return; } else { /* try to recover cp */ mif_err("recover connection: silent reset\n"); link_pm_change_modem_state(pm_data, STATE_CRASH_RESET); } }
static void link_pm_change_modem_state(struct link_pm_data *pm_data, enum modem_state state) { struct modem_ctl *mc = if_usb_get_modemctl(pm_data); if (!mc->iod || pm_data->usb_ld->ld.com_state != COM_ONLINE) return; mif_err("set modem state %d\n", state); mc->iod->modem_state_changed(mc->iod, state); }
static long link_pm_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { int value; struct link_pm_data *pm_data = file->private_data; struct modem_ctl *mc = if_usb_get_modemctl(pm_data); mif_info("%x\n", cmd); switch (cmd) { case IOCTL_LINK_CONTROL_ENABLE: if (copy_from_user(&value, (const void __user *)arg, sizeof(int))) return -EFAULT; if (pm_data->link_ldo_enable) pm_data->link_ldo_enable(!!value); if (pm_data->gpio_link_enable) gpio_set_value(pm_data->gpio_link_enable, value); break; case IOCTL_LINK_CONTROL_ACTIVE: if (copy_from_user(&value, (const void __user *)arg, sizeof(int))) return -EFAULT; gpio_set_value(pm_data->gpio_link_active, value); break; case IOCTL_LINK_GET_HOSTWAKE: return !gpio_get_value(pm_data->gpio_link_hostwake); case IOCTL_LINK_CONNECTED: return pm_data->usb_ld->if_usb_connected; case IOCTL_LINK_SET_BIAS_CLEAR: if (copy_from_user(&value, (const void __user *)arg, sizeof(int))) return -EFAULT; if (value) { gpio_direction_output(pm_data->gpio_link_slavewake, 0); gpio_direction_output(pm_data->gpio_link_hostwake, 0); } else { gpio_direction_output(pm_data->gpio_link_slavewake, 0); gpio_direction_input(pm_data->gpio_link_hostwake); irq_set_irq_type(pm_data->irq_link_hostwake, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING); } case IOCTL_LINK_GET_PHONEACTIVE: return gpio_get_value(mc->gpio_phone_active); default: break; } return 0; }
static void link_pm_force_cp_dump(struct link_pm_data *pm_data) { struct modem_ctl *mc = if_usb_get_modemctl(pm_data); mif_err("Set modem crash ap_dump_int by %pF\n", __builtin_return_address(0)); if (mc->gpio_ap_dump_int) { if (gpio_get_value(mc->gpio_ap_dump_int)) { gpio_set_value(mc->gpio_ap_dump_int, 0); msleep(20); } gpio_set_value(mc->gpio_ap_dump_int, 1); msleep(20); mif_err("AP_DUMP_INT(%d)\n", gpio_get_value(mc->gpio_ap_dump_int)); gpio_set_value(mc->gpio_ap_dump_int, 0); } }
return 0; } static const struct file_operations link_pm_fops = { .owner = THIS_MODULE, .open = link_pm_open, .release = link_pm_release, .unlocked_ioctl = link_pm_ioctl, }; static int link_pm_notifier_event(struct notifier_block *this, unsigned long event, void *ptr) { struct link_pm_data *pm_data = container_of(this, struct link_pm_data, pm_notifier); struct modem_ctl *mc = if_usb_get_modemctl(pm_data); switch (event) { case PM_SUSPEND_PREPARE: #ifdef CONFIG_HIBERNATION case PM_HIBERNATION_PREPARE: case PM_RESTORE_PREPARE: #endif pm_data->dpm_suspending = true; /* set PDA Active High if previous state was LPA */ if (!gpio_get_value(pm_data->gpio_link_active)) { mif_info("PDA active High to LPA suspend spot\n"); gpio_set_value(mc->gpio_pda_active, 1); } mif_debug("dpm suspending set to true\n"); return NOTIFY_OK;