static int i2c_read_bytewise(struct udevice *dev, uint offset, uint8_t *buffer, int len) { struct dm_i2c_chip *chip = dev_get_parent_platdata(dev); struct udevice *bus = dev_get_parent(dev); struct dm_i2c_ops *ops = i2c_get_ops(bus); struct i2c_msg msg[2], *ptr; uint8_t offset_buf[I2C_MAX_OFFSET_LEN]; int ret; int i; for (i = 0; i < len; i++) { if (i2c_setup_offset(chip, offset + i, offset_buf, msg)) return -EINVAL; ptr = msg + 1; ptr->addr = chip->chip_addr; ptr->flags = msg->flags | I2C_M_RD; ptr->len = 1; ptr->buf = &buffer[i]; ptr++; ret = ops->xfer(bus, msg, ptr - msg); if (ret) return ret; } return 0; }
int dm_i2c_read(struct udevice *dev, uint offset, uint8_t *buffer, int len) { struct dm_i2c_chip *chip = dev_get_parent_platdata(dev); struct udevice *bus = dev_get_parent(dev); struct dm_i2c_ops *ops = i2c_get_ops(bus); struct i2c_msg msg[2], *ptr; uint8_t offset_buf[I2C_MAX_OFFSET_LEN]; int msg_count; if (!ops->xfer) return -ENOSYS; if (chip->flags & DM_I2C_CHIP_RD_ADDRESS) return i2c_read_bytewise(dev, offset, buffer, len); ptr = msg; if (!i2c_setup_offset(chip, offset, offset_buf, ptr)) ptr++; if (len) { ptr->addr = chip->chip_addr; ptr->flags = chip->flags & DM_I2C_CHIP_10BIT ? I2C_M_TEN : 0; ptr->flags |= I2C_M_RD; ptr->len = len; ptr->buf = buffer; ptr++; } msg_count = ptr - msg; return ops->xfer(bus, msg, msg_count); }
int i2c_deblock(struct udevice *bus) { struct dm_i2c_ops *ops = i2c_get_ops(bus); if (!ops->deblock) return i2c_deblock_gpio(bus); return ops->deblock(bus); }
int dm_i2c_get_bus_speed(struct udevice *bus) { struct dm_i2c_ops *ops = i2c_get_ops(bus); struct dm_i2c_bus *i2c = dev_get_uclass_priv(bus); if (!ops->get_bus_speed) return i2c->speed_hz; return ops->get_bus_speed(bus); }
int dm_i2c_xfer(struct udevice *dev, struct i2c_msg *msg, int nmsgs) { struct udevice *bus = dev_get_parent(dev); struct dm_i2c_ops *ops = i2c_get_ops(bus); if (!ops->xfer) return -ENOSYS; return ops->xfer(bus, msg, nmsgs); }
int dm_i2c_write(struct udevice *dev, uint offset, const uint8_t *buffer, int len) { struct dm_i2c_chip *chip = dev_get_parent_platdata(dev); struct udevice *bus = dev_get_parent(dev); struct dm_i2c_ops *ops = i2c_get_ops(bus); struct i2c_msg msg[1]; if (!ops->xfer) return -ENOSYS; if (chip->flags & DM_I2C_CHIP_WR_ADDRESS) return i2c_write_bytewise(dev, offset, buffer, len); /* * The simple approach would be to send two messages here: one to * set the offset and one to write the bytes. However some drivers * will not be expecting this, and some chips won't like how the * driver presents this on the I2C bus. * * The API does not support separate offset and data. We could extend * it with a flag indicating that there is data in the next message * that needs to be processed in the same transaction. We could * instead add an additional buffer to each message. For now, handle * this in the uclass since it isn't clear what the impact on drivers * would be with this extra complication. Unfortunately this means * copying the message. * * Use the stack for small messages, malloc() for larger ones. We * need to allow space for the offset (up to 4 bytes) and the message * itself. */ if (len < 64) { uint8_t buf[I2C_MAX_OFFSET_LEN + len]; i2c_setup_offset(chip, offset, buf, msg); msg->len += len; memcpy(buf + chip->offset_len, buffer, len); return ops->xfer(bus, msg, 1); } else { uint8_t *buf; int ret; buf = malloc(I2C_MAX_OFFSET_LEN + len); if (!buf) return -ENOMEM; i2c_setup_offset(chip, offset, buf, msg); msg->len += len; memcpy(buf + chip->offset_len, buffer, len); ret = ops->xfer(bus, msg, 1); free(buf); return ret; } }
int i2c_set_chip_flags(struct udevice *dev, uint flags) { struct udevice *bus = dev->parent; struct dm_i2c_chip *chip = dev_get_parent_platdata(dev); struct dm_i2c_ops *ops = i2c_get_ops(bus); int ret; if (ops->set_flags) { ret = ops->set_flags(dev, flags); if (ret) return ret; } chip->flags = flags; return 0; }
int i2c_deblock(struct udevice *bus) { struct dm_i2c_ops *ops = i2c_get_ops(bus); /* * We could implement a software deblocking here if we could get * access to the GPIOs used by I2C, and switch them to GPIO mode * and then back to I2C. This is somewhat beyond our powers in * driver model at present, so for now just fail. * * See https://patchwork.ozlabs.org/patch/399040/ */ if (!ops->deblock) return -ENOSYS; return ops->deblock(bus); }
static int i2c_mux_bus_xfer(struct udevice *dev, struct i2c_msg *msg, int nmsgs) { struct udevice *mux = dev->parent; struct i2c_mux *priv = dev_get_uclass_priv(mux); struct dm_i2c_ops *ops = i2c_get_ops(priv->i2c_bus); int ret, ret2; debug("%s: %s, bus %s\n", __func__, dev->name, priv->i2c_bus->name); if (!ops->xfer) return -ENOSYS; ret = i2c_mux_select(dev); if (ret) return ret; ret = ops->xfer(priv->i2c_bus, msg, nmsgs); ret2 = i2c_mux_deselect(dev); return ret ? ret : ret2; }
static int i2c_mux_bus_probe(struct udevice *dev, uint chip_addr, uint chip_flags) { struct udevice *mux = dev->parent; struct i2c_mux *priv = dev_get_uclass_priv(mux); struct dm_i2c_ops *ops = i2c_get_ops(priv->i2c_bus); int ret, ret2; debug("%s: %s, bus %s\n", __func__, dev->name, priv->i2c_bus->name); if (!ops->probe_chip) return -ENOSYS; ret = i2c_mux_select(dev); if (ret) return ret; ret = ops->probe_chip(priv->i2c_bus, chip_addr, chip_flags); ret2 = i2c_mux_deselect(dev); return ret ? ret : ret2; }
int dm_i2c_set_bus_speed(struct udevice *bus, unsigned int speed) { struct dm_i2c_ops *ops = i2c_get_ops(bus); struct dm_i2c_bus *i2c = dev_get_uclass_priv(bus); int ret; /* * If we have a method, call it. If not then the driver probably wants * to deal with speed changes on the next transfer. It can easily read * the current speed from this uclass */ if (ops->set_bus_speed) { ret = ops->set_bus_speed(bus, speed); if (ret) return ret; } i2c->speed_hz = speed; return 0; }
static int i2c_write_bytewise(struct udevice *dev, uint offset, const uint8_t *buffer, int len) { struct dm_i2c_chip *chip = dev_get_parent_platdata(dev); struct udevice *bus = dev_get_parent(dev); struct dm_i2c_ops *ops = i2c_get_ops(bus); struct i2c_msg msg[1]; uint8_t buf[I2C_MAX_OFFSET_LEN + 1]; int ret; int i; for (i = 0; i < len; i++) { if (i2c_setup_offset(chip, offset + i, buf, msg)) return -EINVAL; buf[msg->len++] = buffer[i]; ret = ops->xfer(bus, msg, 1); if (ret) return ret; } return 0; }
/** * i2c_probe_chip() - probe for a chip on a bus * * @bus: Bus to probe * @chip_addr: Chip address to probe * @flags: Flags for the chip * @return 0 if found, -ENOSYS if the driver is invalid, -EREMOTEIO if the chip * does not respond to probe */ static int i2c_probe_chip(struct udevice *bus, uint chip_addr, enum dm_i2c_chip_flags chip_flags) { struct dm_i2c_ops *ops = i2c_get_ops(bus); struct i2c_msg msg[1]; int ret; if (ops->probe_chip) { ret = ops->probe_chip(bus, chip_addr, chip_flags); if (!ret || ret != -ENOSYS) return ret; } if (!ops->xfer) return -ENOSYS; /* Probe with a zero-length message */ msg->addr = chip_addr; msg->flags = chip_flags & DM_I2C_CHIP_10BIT ? I2C_M_TEN : 0; msg->len = 0; msg->buf = NULL; return ops->xfer(bus, msg, 1); }