i2c: avoid dynamic stack use in dm_i2c_write

The size of the dynamic stack allocation here is bounded by the if()
statement. However, just allocating the maximum size up-front and
doing malloc() if necessary avoids code duplication (the
i2c_setup_offset() until the invocation of ->xfer), and generates much
better (smaller) code:

bloat-o-meter drivers/i2c/i2c-uclass.o.{0,1}
add/remove: 0/0 grow/shrink: 0/1 up/down: 0/-144 (-144)
Function                                     old     new   delta
dm_i2c_write                                 552     408    -144
Total: Before=3828, After=3684, chg -3.76%

It also makes static analysis of maximum stack usage (using the .su
files that are automatically generated during build) easier if there
are no lines saying "dynamic".

[This is not entirely equivalent to the existing code; this now uses
the stack for len <= 64 rather than len <= 63, but that seems like a
more natural limit.]

Signed-off-by: Rasmus Villemoes <rasmus.villemoes@prevas.dk>
Reviewed-by: Heiko Schocher <hs@denx.de>
This commit is contained in:
Rasmus Villemoes 2022-07-07 15:12:17 +02:00 committed by Heiko Schocher
parent 2b77eea7f3
commit 49f3a42edf

View file

@ -168,6 +168,9 @@ int dm_i2c_write(struct udevice *dev, uint offset, const uint8_t *buffer,
struct udevice *bus = dev_get_parent(dev); struct udevice *bus = dev_get_parent(dev);
struct dm_i2c_ops *ops = i2c_get_ops(bus); struct dm_i2c_ops *ops = i2c_get_ops(bus);
struct i2c_msg msg[1]; struct i2c_msg msg[1];
uint8_t _buf[I2C_MAX_OFFSET_LEN + 64];
uint8_t *buf = _buf;
int ret;
if (!ops->xfer) if (!ops->xfer)
return -ENOSYS; return -ENOSYS;
@ -192,29 +195,20 @@ int dm_i2c_write(struct udevice *dev, uint offset, const uint8_t *buffer,
* need to allow space for the offset (up to 4 bytes) and the message * need to allow space for the offset (up to 4 bytes) and the message
* itself. * itself.
*/ */
if (len < 64) { if (len > sizeof(_buf) - I2C_MAX_OFFSET_LEN) {
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); buf = malloc(I2C_MAX_OFFSET_LEN + len);
if (!buf) if (!buf)
return -ENOMEM; return -ENOMEM;
}
i2c_setup_offset(chip, offset, buf, msg); i2c_setup_offset(chip, offset, buf, msg);
msg->len += len; msg->len += len;
memcpy(buf + chip->offset_len, buffer, len); memcpy(buf + chip->offset_len, buffer, len);
ret = ops->xfer(bus, msg, 1); ret = ops->xfer(bus, msg, 1);
if (buf != _buf)
free(buf); free(buf);
return ret; return ret;
}
} }
int dm_i2c_xfer(struct udevice *dev, struct i2c_msg *msg, int nmsgs) int dm_i2c_xfer(struct udevice *dev, struct i2c_msg *msg, int nmsgs)