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 int vnet_xmit(struct sk_buff *skb, struct net_device *ndev) { struct vnet *vn = netdev_priv(ndev); struct modemctl *mc = vn->mc; unsigned long flags; spin_lock_irqsave(&mc->lock, flags); if (readl(mc->mmio + OFF_SEM) & 1) { /* if we happen to hold the hw mmio sem, transmit NOW */ if (handle_raw_tx(mc, skb)) { wake_lock(&mc->ip_tx_wakelock); skb_queue_tail(&vn->txq, skb); } else { MODEM_COUNT(mc, tx_no_delay); } if (!mc->mmio_owner) { /* if we don't own the semaphore, immediately * give it back to the modem and signal the modem * to process the packet */ writel(0, mc->mmio + OFF_SEM); writel(MB_VALID | MBD_SEND_RAW, mc->mmio + OFF_MBOX_AP); MODEM_COUNT(mc, tx_bp_signaled); } } else { /* otherwise request the hw mmio sem and queue */ modem_request_sem(mc); skb_queue_tail(&vn->txq, skb); MODEM_COUNT(mc, tx_queued); } spin_unlock_irqrestore(&mc->lock, flags); return NETDEV_TX_OK; }
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; }