int modem_request_mmio(struct modemctl *mc) { unsigned long flags; int ret; spin_lock_irqsave(&mc->lock, flags); mc->mmio_req_count++; ret = mc->mmio_owner; if (!ret) { if (mmio_sem(mc) == 1) { /* surprise! we already have control */ ret = mc->mmio_owner = 1; wake_up(&mc->wq); modem_update_state(mc); MODEM_COUNT(mc,request_no_wait); } else { /* ask the modem for mmio access */ if (modem_running(mc)) modem_request_sem(mc); MODEM_COUNT(mc,request_wait); } } else { MODEM_COUNT(mc,request_no_wait); } /* TODO: timer to retry? */ spin_unlock_irqrestore(&mc->lock, flags); return ret; }
static ssize_t modemctl_write(struct file *filp, const char __user *buf, size_t count, loff_t *ppos) { struct modemctl *mc = filp->private_data; u32 owner; char *data; loff_t pos = *ppos; unsigned long ret; mutex_lock(&mc->ctl_lock); data = (char __force *)mc->mmio + pos; owner = mmio_sem(mc); if (mc->status != MODEM_POWER_ON) { pr_err("modemctl_write: modem not powered on\n"); ret = -EINVAL; goto done; } if (!owner) { pr_err("modemctl_write: doesn't own semaphore\n"); ret = -EIO; goto done; } if (pos < 0) { ret = -EINVAL; goto done; } if (pos >= mc->mmsize) { ret = -EINVAL; goto done; } if (count > mc->mmsize - pos) count = mc->mmsize - pos; ret = copy_from_user(data, buf, count); if (ret) { ret = -EFAULT; goto done; } *ppos = pos + count; ret = count; done: mutex_unlock(&mc->ctl_lock); return ret; }
static int modem_start(struct modemctl *mc, int ramdump) { pr_info("[MODEM] modem_start() %s\n", ramdump ? "ramdump" : "normal"); if (mc->status != MODEM_POWER_ON) { pr_err("[MODEM] modem not powered on\n"); return -EINVAL; } if (readl(mc->mmio + OFF_MBOX_BP) != MODEM_MSG_SBL_DONE) { pr_err("[MODEM] bootloader not ready\n"); return -EIO; } if (mmio_sem(mc) != 1) { pr_err("[MODEM] we do not own the semaphore\n"); return -EIO; } writel(0, mc->mmio + OFF_SEM); if (ramdump) { mc->status = MODEM_BOOTING_RAMDUMP; mc->ramdump_size = 0; mc->ramdump_pos = 0; writel(MODEM_CMD_RAMDUMP_START, mc->mmio + OFF_MBOX_AP); /* TODO: timeout and fail */ wait_event(mc->wq, mc->status == MODEM_DUMPING); } else { mc->status = MODEM_BOOTING_NORMAL; writel(MODEM_CMD_BINARY_LOAD, mc->mmio + OFF_MBOX_AP); /* TODO: timeout and fail */ wait_event(mc->wq, modem_running(mc)); } pr_info("[MODEM] modem_start() DONE\n"); return 0; }
static irqreturn_t modemctl_mbox_irq_handler(int irq, void *_mc) { struct modemctl *mc = _mc; unsigned cmd; unsigned long flags; cmd = readl(mc->mmio + OFF_MBOX_BP); if (unlikely(mc->status != MODEM_RUNNING)) { modemctl_handle_offline(mc, cmd); return IRQ_HANDLED; } if (!(cmd & MB_VALID)) { if (cmd == MODEM_MSG_LOGDUMP_DONE) { pr_info("modem: logdump done!\n"); mc->logdump_data = 1; wake_up(&mc->wq); } else { pr_info("modem: what is %08x\n",cmd); } return IRQ_HANDLED; } spin_lock_irqsave(&mc->lock, flags); if (cmd & MB_COMMAND) { switch (cmd & 15) { case MBC_REQ_SEM: if (mmio_sem(mc) == 0) { /* Sometimes the modem may ask for the * sem when it already owns it. Humor * it and ack that request. */ writel(MB_COMMAND | MB_VALID | MBC_RES_SEM, mc->mmio + OFF_MBOX_AP); MODEM_COUNT(mc,bp_req_confused); } else if (mc->mmio_req_count == 0) { /* No references? Give it to the modem. */ modem_update_state(mc); mc->mmio_owner = 0; writel(0, mc->mmio + OFF_SEM); writel(MB_COMMAND | MB_VALID | MBC_RES_SEM, mc->mmio + OFF_MBOX_AP); MODEM_COUNT(mc,bp_req_instant); goto done; } else { /* Busy now, remember the modem needs it. */ mc->mmio_bp_request = 1; MODEM_COUNT(mc,bp_req_delayed); break; } case MBC_RES_SEM: break; case MBC_PHONE_START: /* TODO: should we avoid sending any other messages * to the modem until this message is received and * acknowledged? */ writel(MB_COMMAND | MB_VALID | MBC_INIT_END | CP_BOOT_AIRPLANE | AP_OS_ANDROID, mc->mmio + OFF_MBOX_AP); /* TODO: probably unsafe to send this back-to-back * with the INIT_END message. */ /* if somebody is waiting for mmio access... */ if (mc->mmio_req_count) modem_request_sem(mc); break; case MBC_RESET: pr_err("$$$ MODEM RESET $$$\n"); mc->status = MODEM_CRASHED; wake_up(&mc->wq); break; case MBC_ERR_DISPLAY: { char buf[SIZ_ERROR_MSG + 1]; int i; pr_err("$$$ MODEM ERROR $$$\n"); mc->status = MODEM_CRASHED; wake_up(&mc->wq); memcpy(buf, mc->mmio + OFF_ERROR_MSG, SIZ_ERROR_MSG); for (i = 0; i < SIZ_ERROR_MSG; i++) if ((buf[i] < 0x20) || (buf[1] > 0x7e)) buf[i] = 0x20; buf[i] = 0; i--; while ((i > 0) && (buf[i] == 0x20)) buf[i--] = 0; pr_err("$$$ %s $$$\n", buf); break; } case MBC_SUSPEND: break; case MBC_RESUME: break; } } /* On *any* interrupt from the modem it may have given * us ownership of the mmio hw semaphore. If that * happens, we should claim the semaphore if we have * threads waiting for it and we should process any * messages that the modem has enqueued in its fifos * by calling modem_handle_io(). */ if (mmio_sem(mc) == 1) { if (!mc->mmio_owner) { modem_update_state(mc); if (mc->mmio_req_count) { mc->mmio_owner = 1; wake_up(&mc->wq); } } modem_handle_io(mc); /* If we have a signal to send and we're not * hanging on to the mmio hw semaphore, give * it back to the modem and send the signal. * Otherwise this will happen when we give up * the mmio hw sem in modem_release_mmio(). */ if (mc->mmio_signal_bits && !mc->mmio_owner) { writel(0, mc->mmio + OFF_SEM); writel(MB_VALID | mc->mmio_signal_bits, mc->mmio + OFF_MBOX_AP); mc->mmio_signal_bits = 0; } } done: spin_unlock_irqrestore(&mc->lock, flags); return IRQ_HANDLED; }