static irqreturn_t smu_db_intr()

in smu.c [161:241]


static irqreturn_t smu_db_intr(int irq, void *arg)
{
	unsigned long flags;
	struct smu_cmd *cmd;
	void (*done)(struct smu_cmd *cmd, void *misc) = NULL;
	void *misc = NULL;
	u8 gpio;
	int rc = 0;

	/* SMU completed the command, well, we hope, let's make sure
	 * of it
	 */
	spin_lock_irqsave(&smu->lock, flags);

	gpio = pmac_do_feature_call(PMAC_FTR_READ_GPIO, NULL, smu->doorbell);
	if ((gpio & 7) != 7) {
		spin_unlock_irqrestore(&smu->lock, flags);
		return IRQ_HANDLED;
	}

	cmd = smu->cmd_cur;
	smu->cmd_cur = NULL;
	if (cmd == NULL)
		goto bail;

	if (rc == 0) {
		unsigned long faddr;
		int reply_len;
		u8 ack;

		/* CPU might have brought back the cache line, so we need
		 * to flush again before peeking at the SMU response. We
		 * flush the entire buffer for now as we haven't read the
		 * reply length (it's only 2 cache lines anyway)
		 */
		faddr = (unsigned long)smu->cmd_buf;
		flush_dcache_range(faddr, faddr + 256);

		/* Now check ack */
		ack = (~cmd->cmd) & 0xff;
		if (ack != smu->cmd_buf->cmd) {
			DPRINTK("SMU: incorrect ack, want %x got %x\n",
				ack, smu->cmd_buf->cmd);
			rc = -EIO;
		}
		reply_len = rc == 0 ? smu->cmd_buf->length : 0;
		DPRINTK("SMU: reply len: %d\n", reply_len);
		if (reply_len > cmd->reply_len) {
			printk(KERN_WARNING "SMU: reply buffer too small,"
			       "got %d bytes for a %d bytes buffer\n",
			       reply_len, cmd->reply_len);
			reply_len = cmd->reply_len;
		}
		cmd->reply_len = reply_len;
		if (cmd->reply_buf && reply_len)
			memcpy(cmd->reply_buf, smu->cmd_buf->data, reply_len);
	}

	/* Now complete the command. Write status last in order as we lost
	 * ownership of the command structure as soon as it's no longer -1
	 */
	done = cmd->done;
	misc = cmd->misc;
	mb();
	cmd->status = rc;

	/* Re-enable NAP mode */
	if (smu->broken_nap)
		powersave_nap = 1;
 bail:
	/* Start next command if any */
	smu_start_cmd();
	spin_unlock_irqrestore(&smu->lock, flags);

	/* Call command completion handler if any */
	if (done)
		done(cmd, misc);

	/* It's an edge interrupt, nothing to do */
	return IRQ_HANDLED;
}