static void start_link_pm(struct modem_link_pm *pm, enum pm_event event) { int cp2ap_wakeup; int cp2ap_status; enum pm_state state; unsigned long flags; spin_lock_irqsave(&pm->lock, flags); if (pm->active) { mif_err("%s: PM is already ACTIVE\n", pm->link_name); goto exit; } lock_pm_wake(pm); if (event == PM_EVENT_LOCK_ON) { state = PM_STATE_LOCKED_ON; assert_ap2cp_wakeup(pm); assert_ap2cp_status(pm); } else if (event == PM_EVENT_CP_BOOTING) { state = PM_STATE_CP_BOOTING; assert_ap2cp_wakeup(pm); assert_ap2cp_status(pm); } else { state = PM_STATE_UNMOUNTED; release_ap2cp_wakeup(pm); release_ap2cp_status(pm); } /* Enable every CP-to-AP IRQ and set it as a wake-up source */ cp2ap_wakeup = gpio_get_value(pm->gpio_cp2ap_wakeup); change_irq_level(pm->cp2ap_wakeup_irq.num, cp2ap_wakeup); mif_enable_irq(&pm->cp2ap_wakeup_irq); cp2ap_status = gpio_get_value(pm->gpio_cp2ap_status); change_irq_level(pm->cp2ap_status_irq.num, cp2ap_status); mif_enable_irq(&pm->cp2ap_status_irq); set_pm_fsm(pm, PM_STATE_UNMOUNTED, state, event); pm->hold_requested = false; pm->active = true; #ifdef DEBUG_MODEM_IF print_pm_fsm(pm); #endif exit: spin_unlock_irqrestore(&pm->lock, flags); }
static irqreturn_t cp2ap_status_handler(int irq, void *data) { struct modem_link_pm *pm = (struct modem_link_pm *)data; int cp2ap_status = gpio_get_value(pm->gpio_cp2ap_status); if (cp2ap_status) run_pm_fsm(pm, PM_EVENT_CP2AP_STATUS_HIGH); else run_pm_fsm(pm, PM_EVENT_CP2AP_STATUS_LOW); change_irq_level(irq, cp2ap_status); return IRQ_HANDLED; }
/** @brief interrupt handler for a wakeup interrupt 1) Reads the interrupt value\n 2) Performs interrupt handling\n @param irq the IRQ number @param data the pointer to a data */ static irqreturn_t cp2ap_wakeup_handler(int irq, void *data) { struct modem_link_pm *pm = (struct modem_link_pm *)data; int cp2ap_wakeup = gpio_get_value(pm->gpio_cp2ap_wakeup); mif_debug("%s: cp2ap_wakeup[%d]\n", __func__, cp2ap_wakeup); if (cp2ap_wakeup) { run_pm_fsm(pm, PM_EVENT_CP2AP_WAKEUP_HIGH); } else { run_pm_fsm(pm, PM_EVENT_CP2AP_WAKEUP_LOW); } change_irq_level(irq, cp2ap_wakeup); return IRQ_HANDLED; }
int init_link_device_pm(struct link_device *ld, struct modem_link_pm *pm, struct link_pm_svc *pm_svc, void (*fail_fn)(struct modem_link_pm *), void (*cp_fail_fn)(struct modem_link_pm *)) { struct modem_data *mdata = ld->mdm_data; int err; int cp2ap_wakeup; int cp2ap_status; unsigned int num; unsigned long flags; char name[MAX_NAME_LEN]; /* Set up variables for PM */ pm->link_name = ld->name; pm->fail_handler = fail_fn; pm->cp_fail_handler = cp_fail_fn; /* Retrieve GPIO pins and IRQ numbers for PM */ pm->gpio_cp2ap_wakeup = mdata->gpio_ap_wakeup; pm->gpio_ap2cp_wakeup = mdata->gpio_cp_wakeup; pm->gpio_cp2ap_status = mdata->gpio_cp_status; pm->gpio_ap2cp_status = mdata->gpio_ap_status; num = gpio_to_irq(pm->gpio_cp2ap_wakeup); flags = IRQF_NO_THREAD | IRQF_NO_SUSPEND | IRQF_ONESHOT; snprintf(name, MAX_NAME_LEN, "%s_cp2ap_wakeup", pm->link_name); mif_init_irq(&pm->cp2ap_wakeup_irq, num, name, flags); num = gpio_to_irq(pm->gpio_cp2ap_status); flags = IRQF_NO_THREAD | IRQF_NO_SUSPEND | IRQF_ONESHOT; snprintf(name, MAX_NAME_LEN, "%s_cp2ap_status", pm->link_name); mif_init_irq(&pm->cp2ap_status_irq, num, name, flags); mif_err("CP2AP_WAKEUP GPIO:%d IRQ:%d\n", pm->gpio_cp2ap_wakeup, pm->cp2ap_wakeup_irq.num); mif_err("AP2CP_WAKEUP GPIO:%d\n", pm->gpio_ap2cp_wakeup); mif_err("CP2AP_STATUS GPIO:%d IRQ:%d\n", pm->gpio_cp2ap_status, pm->cp2ap_status_irq.num); mif_err("AP2CP_STATUS GPIO:%d\n", pm->gpio_ap2cp_status); /* Register cp2ap_wakeup IRQ handler */ cp2ap_wakeup = gpio_get_value(pm->gpio_cp2ap_wakeup); change_irq_level(pm->cp2ap_wakeup_irq.num, cp2ap_wakeup); err = mif_request_irq(&pm->cp2ap_wakeup_irq, cp2ap_wakeup_handler, pm); if (err) { mif_err("%s: ERR! request_irq(%s#%d) fail (%d)\n", pm->link_name, pm->cp2ap_wakeup_irq.name, pm->cp2ap_wakeup_irq.num, err); return err; } mif_disable_irq(&pm->cp2ap_wakeup_irq); mif_err("%s: %s_irq#%d handler registered\n", pm->link_name, pm->cp2ap_wakeup_irq.name, pm->cp2ap_wakeup_irq.num); /* Register cp2ap_status IRQ handler */ cp2ap_status = gpio_get_value(pm->gpio_cp2ap_status); change_irq_level(pm->cp2ap_status_irq.num, cp2ap_status); err = mif_request_irq(&pm->cp2ap_status_irq, cp2ap_status_handler, pm); if (err) { mif_err("%s: ERR! request_irq(%s#%d) fail (%d)\n", pm->link_name, pm->cp2ap_status_irq.name, pm->cp2ap_status_irq.num, err); free_irq(pm->cp2ap_wakeup_irq.num, pm); return err; } mif_disable_irq(&pm->cp2ap_status_irq); mif_err("%s: %s_irq#%d handler registered\n", pm->link_name, pm->cp2ap_status_irq.name, pm->cp2ap_status_irq.num); /* Initialize common variables for PM */ spin_lock_init(&pm->lock); snprintf(pm->wlock_name, MAX_NAME_LEN, "%s_pm_wlock", pm->link_name); wake_lock_init(&pm->wlock, WAKE_LOCK_SUSPEND, pm->wlock_name); snprintf(pm->wq_name, MAX_NAME_LEN, "%s_pm_wq", pm->link_name); flags = WQ_NON_REENTRANT | WQ_UNBOUND | WQ_HIGHPRI; pm->wq = alloc_workqueue(pm->wq_name, flags, 1); if (!pm->wq) { mif_err("%s: ERR! fail to create %s\n", pm->link_name, pm->wq_name); return -EFAULT; } INIT_DELAYED_WORK(&pm->cp_free_dwork, cp_free_work_func); init_pm_fsm(pm); /* Register PM functions set by the common link PM framework and used by each link device driver */ pm->start = start_link_pm; pm->stop = stop_link_pm; pm->request_hold = request_hold; pm->release_hold = release_hold; pm->link_active = link_active; return 0; }