int mcp2210_irq_probe(struct mcp2210_device *dev) { uint i; mutex_init(&dev->irq_lock); dev->nr_irqs = 0; for (i = 0; i < 9; ++i) { struct mcp2210_pin_config *pin = &dev->config->pins[i]; if (!pin->has_irq) continue; switch (pin->mode) { case MCP2210_PIN_DEDICATED: if (i != 6) return -EINVAL; dev->s.poll_intr = 1; break; case MCP2210_PIN_GPIO: dev->s.poll_gpio = 1; break; }; if (pin->irq + 1> dev->nr_irqs) dev->nr_irqs = pin->irq + 1; } BUG_ON(dev->nr_irqs > 7); #ifdef CONFIG_MCP2210_GPIO if (dev->s.poll_gpio) { ctl_cmd_init(dev, &dev->cmd_poll_gpio, MCP2210_CMD_GET_PIN_VALUE, 0, NULL, 0, false); dev->cmd_poll_gpio.head.complete = complete_poll; mcp2210_add_cmd(&dev->cmd_poll_gpio.head, false); } #endif /* CONFIG_MCP2210_GPIO */ if (dev->s.poll_intr) { ctl_cmd_init(dev, &dev->cmd_poll_intr, MCP2210_CMD_GET_INTERRUPTS, 0, NULL, 0, false); dev->cmd_poll_intr.head.complete = complete_poll; mcp2210_add_cmd(&dev->cmd_poll_intr.head, false); } return 0; }
static int complete_poll(struct mcp2210_cmd *cmd_head, void *context) { struct mcp2210_device *dev = cmd_head->dev; struct mcp2210_cmd_ctl *cmd = (void*)cmd_head; unsigned long now = jiffies; int enabled; unsigned long interval; unsigned long interval_j; unsigned long next; long next_diff; mcp2210_debug(); if (dev->dead) return -EINPROGRESS; if (cmd->req.cmd == MCP2210_CMD_GET_PIN_VALUE) { enabled = dev->poll_gpio; interval = dev->config->poll_gpio_usecs; dev->last_poll_gpio = now; } else { enabled = dev->poll_intr; interval = dev->config->poll_intr_usecs; dev->last_poll_intr = now; } if (dev->suppress_poll_warn && jiffdiff(dev->suppress_poll_warn, now) <= 0) dev->suppress_poll_warn = 0; if (!enabled) return -EINPROGRESS; interval_j = usecs_to_jiffies(interval); next = dev->eps[EP_OUT].submit_time + interval_j; next_diff = jiffdiff(next, now); cmd->head.delayed = 1; if (next_diff < 0) { next = now + interval_j; if (!dev->suppress_poll_warn) warn_poll_past_due(dev, now, -next_diff); } cmd->head.delay_until = next; #if 0 mcp2210_debug("interval_j: %lu, submit_time: %lu, next: %lu, jiffies: %lu", interval_j, dev->eps[EP_OUT].submit_time, next, jiffies); #endif cmd->head.state = MCP2210_STATE_NEW; mcp2210_add_cmd(cmd_head, false); return -EINPROGRESS; /* tell process_commands not to free us */ }
static int complete_poll(struct mcp2210_cmd *cmd_head, void *context) { struct mcp2210_device *dev = cmd_head->dev; struct mcp2210_cmd_ctl *cmd = (void*)cmd_head; int enabled; unsigned long interval; unsigned long now = jiffies; mcp2210_debug(); if (dev->dead) return -EINPROGRESS; if (cmd->req.cmd == MCP2210_CMD_GET_PIN_VALUE) { enabled = dev->s.poll_gpio; interval = dev->config->poll_gpio_usecs; dev->s.last_poll_gpio = now; } else { enabled = dev->s.poll_intr; interval = dev->config->poll_intr_usecs; dev->s.last_poll_intr = now; } if (!enabled) return -EINPROGRESS; if (1) { unsigned long interval_j = usecs_to_jiffies(interval); unsigned long next = dev->eps[EP_OUT].submit_time + interval_j; cmd->head.delayed = 1; if (jiffdiff(next, now) < 0) { mcp2210_warn("poll interval collapse, restarting universe"); next = jiffies + interval_j; } cmd->head.delay_until = next; #if 0 mcp2210_debug("interval_j: %lu, submit_time: %lu, next: %lu, jiffies: %lu", interval_j, dev->eps[EP_OUT].submit_time, next, jiffies); #endif } cmd->head.state = MCP2210_STATE_NEW; mcp2210_add_cmd(cmd_head, false); return -EINPROGRESS; /* tell process_commands not to free us */ }
/****************************************************************************** * probe & remove */ int mcp2210_irq_probe(struct mcp2210_device *dev) { uint i; int ret; mcp2210_info(); mutex_init(&dev->irq_lock); dev->nr_irqs = 0; dev->poll_intr = 0; dev->poll_gpio = 0; for (i = 0; i < MCP2210_NUM_PINS; ++i) { const struct mcp2210_pin_config *pin = &dev->config->pins[i]; if (pin->mode == MCP2210_PIN_SPI || !pin->has_irq) continue; ++dev->nr_irqs; BUG_ON(dev->irq_revmap[i]); dev->irq_revmap[i] = pin->irq; if (pin->mode == MCP2210_PIN_DEDICATED) dev->poll_intr = 1; else if (pin->mode == MCP2210_PIN_GPIO) { dev->poll_gpio = 1; dev->irq_type[i] = pin->irq_type; } } if (!dev->nr_irqs) return 0; ret = irq_alloc_descs(-1, 0, dev->nr_irqs, 0); if (ret < 0) { /* CONFIG_SPARSE_IRQ needed? */ mcp2210_err("Failed to allocate %u irq descriptors: %d", dev->nr_irqs, ret); return ret; } dev->irq_base = ret; for (i = 0; i < dev->nr_irqs; ++i) { int virq = dev->irq_base + i; dev->irq_descs[i] = irq_to_desc(virq); BUG_ON(!dev->irq_descs[i]); irq_set_chip_data(virq, dev); irq_set_chip(virq, &mcp2210_irq_chip); #if defined(CONFIG_ARM) && LINUX_VERSION_CODE < KERNEL_VERSION(4,3,0) set_irq_flags(virq, 0); #else irq_set_noprobe(virq); #endif } #ifdef CONFIG_MCP2210_GPIO if (dev->poll_gpio) { ctl_cmd_init(dev, &dev->cmd_poll_gpio, MCP2210_CMD_GET_PIN_VALUE, 0, NULL, 0, false); dev->cmd_poll_gpio.head.complete = complete_poll; mcp2210_add_cmd(&dev->cmd_poll_gpio.head, false); } #endif /* CONFIG_MCP2210_GPIO */ if (dev->poll_intr) { /* read and then reset */ ctl_cmd_init(dev, &dev->cmd_poll_intr, MCP2210_CMD_GET_INTERRUPTS, 0, NULL, 0, false); dev->cmd_poll_intr.head.complete = complete_poll; mcp2210_add_cmd(&dev->cmd_poll_intr.head, false); } dev->is_irq_probed = 1; dev->suppress_poll_warn = 0; return 0; }