/** * prcmu_abb_read() - Read register value(s) from the ABB. * @slave: The I2C slave address. * @reg: The (start) register address. * @value: The read out value(s). * @size: The number of registers to read. * * Reads register value(s) from the ABB. * @size has to be 1 for the current firmware version. */ int prcmu_abb_read(u8 slave, u8 reg, u8 *value, u8 size) { int r; if (size != 1) return -EINVAL; r = mutex_lock_interruptible(&mb5_transfer.lock); if (r) return r; while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(5)) cpu_relax(); writeb(I2C_READ(slave), REQ_MB5_I2C_SLAVE_OP); writeb(I2C_STOP_EN, REQ_MB5_I2C_HW_BITS); writeb(reg, REQ_MB5_I2C_REG); writel(MBOX_BIT(5), PRCM_MBOX_CPU_SET); if (!wait_for_completion_timeout(&mb5_transfer.work, msecs_to_jiffies(500))) { pr_err("prcmu: prcmu_abb_read timed out.\n"); r = -EIO; goto unlock_and_return; } r = ((mb5_transfer.ack.status == I2C_RD_OK) ? 0 : -EIO); if (!r) *value = mb5_transfer.ack.value; unlock_and_return: mutex_unlock(&mb5_transfer.lock); return r; }
int db5500_prcmu_abb_write(u8 slave, u8 reg, u8 *value, u8 size) { int r; if ((size < 1) || (4 < size)) return -EINVAL; mutex_lock(&mb5_transfer.lock); while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(5)) cpu_relax(); writeb(slave, PRCM_REQ_MB5_I2C_SLAVE); writeb(reg, PRCM_REQ_MB5_I2C_REG); writeb(size, PRCM_REQ_MB5_I2C_SIZE); memcpy_toio(PRCM_REQ_MB5_I2C_DATA, value, size); writeb(MB5H_I2C_WRITE, PRCM_REQ_MB5_HEADER); writel(MBOX_BIT(5), PRCM_MBOX_CPU_SET); wait_for_completion(&mb5_transfer.work); if ((mb5_transfer.ack.header == MB5H_I2C_WRITE) && (mb5_transfer.ack.status == RC_SUCCESS)) r = 0; else r = -EIO; mutex_unlock(&mb5_transfer.lock); return r; }
static void ack_dbb_wakeup(void) { unsigned long flags; spin_lock_irqsave(&mb0_transfer.lock, flags); while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(0)) cpu_relax(); writeb(RMB0H_RD_WAKE_UP_ACK, PRCM_REQ_MB0_HEADER); writel(MBOX_BIT(0), PRCM_MBOX_CPU_SET); spin_unlock_irqrestore(&mb0_transfer.lock, flags); }
static void read_mailbox_5(void) { mb5_transfer.ack.status = readb(ACK_MB5_I2C_STATUS); mb5_transfer.ack.value = readb(ACK_MB5_I2C_VAL); complete(&mb5_transfer.work); writel(MBOX_BIT(5), PRCM_ARM_IT1_CLEAR); }
static irqreturn_t prcmu_irq_handler(int irq, void *data) { u32 bits; u8 n; bits = (readl(PRCM_ARM_IT1_VAL) & (MBOX_BIT(NUM_MBOX) - 1)); if (unlikely(!bits)) return IRQ_NONE; for (n = 0; bits; n++) { if (bits & MBOX_BIT(n)) { bits -= MBOX_BIT(n); read_mailbox[n](); } } return IRQ_HANDLED; }
static int __init prcmu_init(void) { mutex_init(&mb5_transfer.lock); init_completion(&mb5_transfer.work); /* Clean up the mailbox interrupts after pre-kernel code. */ writel((MBOX_BIT(NUM_MBOX) - 1), PRCM_ARM_IT1_CLEAR); return request_irq(IRQ_PRCMU, prcmu_irq_handler, 0, "prcmu", NULL); }
static irqreturn_t prcmu_irq_handler(int irq, void *data) { u32 bits; u8 n; irqreturn_t r; bits = (readl(PRCM_ARM_IT1_VAL) & ALL_MBOX_BITS); if (unlikely(!bits)) return IRQ_NONE; r = IRQ_HANDLED; for (n = 0; bits; n++) { if (bits & MBOX_BIT(n)) { bits -= MBOX_BIT(n); if (read_mailbox[n]()) r = IRQ_WAKE_THREAD; } } return r; }
static bool read_mailbox_0(void) { bool r; u8 header; header = readb(PRCM_ACK_MB0_HEADER); switch (header) { case AMB0H_WAKE_UP: r = true; break; default: print_unknown_header_warning(0, header); r = false; break; } writel(MBOX_BIT(0), PRCM_ARM_IT1_CLR); return r; }
static bool read_mailbox_5(void) { u8 header; header = readb(PRCM_ACK_MB5_HEADER); switch (header) { case MB5H_I2C_READ: memcpy_fromio(mb5_transfer.ack.value, PRCM_ACK_MB5_I2C_DATA, 4); case MB5H_I2C_WRITE: mb5_transfer.ack.header = header; mb5_transfer.ack.status = readb(PRCM_ACK_MB5_RETURN_CODE); complete(&mb5_transfer.work); break; default: print_unknown_header_warning(5, header); break; } writel(MBOX_BIT(5), PRCM_ARM_IT1_CLR); return false; }
static bool read_mailbox_7(void) { writel(MBOX_BIT(7), PRCM_ARM_IT1_CLR); return false; }
static bool read_mailbox_6(void) { writel(MBOX_BIT(6), PRCM_ARM_IT1_CLEAR); return false; }
static void read_mailbox_7(void) { writel(MBOX_BIT(7), PRCM_ARM_IT1_CLEAR); }