/* * I2C read called by cmd_i2c when doing 'i2c read' and by cmd_eeprom.c * Begin write, send address byte(s), begin read, receive data bytes, end. */ int i2c_read(u8 dev, uint addr, int alen, u8 *data, int length) { u32 status; u32 i = 0; u8 *cur_data = data; /* Check the hardware can handle the requested bytes */ if ((length < 0) || (length > ZYNQ_I2C_TRANSFERT_SIZE_MAX)) return -EINVAL; /* Write the register address */ setbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_CLR_FIFO | ZYNQ_I2C_CONTROL_HOLD); /* * Temporarily disable restart (by clearing hold) * It doesn't seem to work. */ clrbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_RW | ZYNQ_I2C_CONTROL_HOLD); writel(0xFF, &zynq_i2c->interrupt_status); while (alen--) writel(addr >> (8*alen), &zynq_i2c->data); writel(dev, &zynq_i2c->address); /* Wait for the address to be sent */ if (!zynq_i2c_wait(ZYNQ_I2C_INTERRUPT_COMP)) { /* Release the bus */ clrbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_HOLD); return -ETIMEDOUT; } debug("Device acked address\n"); setbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_CLR_FIFO | ZYNQ_I2C_CONTROL_RW); /* Start reading data */ writel(dev, &zynq_i2c->address); writel(length, &zynq_i2c->transfer_size); /* Wait for data */ do { status = zynq_i2c_wait(ZYNQ_I2C_INTERRUPT_COMP | ZYNQ_I2C_INTERRUPT_DATA); if (!status) { /* Release the bus */ clrbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_HOLD); return -ETIMEDOUT; } debug("Read %d bytes\n", length - readl(&zynq_i2c->transfer_size)); for (; i < length - readl(&zynq_i2c->transfer_size); i++) *(cur_data++) = readl(&zynq_i2c->data); } while (readl(&zynq_i2c->transfer_size) != 0); /* All done... release the bus */ clrbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_HOLD); #ifdef DEBUG zynq_i2c_debug_status(); #endif return 0; }
/* Wait for an interrupt */ static u32 zynq_i2c_wait(struct zynq_i2c_registers *zynq_i2c, u32 mask) { int timeout, int_status; for (timeout = 0; timeout < 100; timeout++) { udelay(100); int_status = readl(&zynq_i2c->interrupt_status); if (int_status & mask) break; } #ifdef DEBUG zynq_i2c_debug_status(zynq_i2c); #endif /* Clear interrupt status flags */ writel(int_status & mask, &zynq_i2c->interrupt_status); return int_status & mask; }