/** * meson_spifc_wait_ready() - wait for the current operation to terminate * @spifc: the Meson SPI device * Return: 0 on success, a negative value on error
*/ staticint meson_spifc_wait_ready(struct meson_spifc *spifc)
{ unsignedlong deadline = jiffies + msecs_to_jiffies(5);
u32 data;
do {
regmap_read(spifc->regmap, REG_SLAVE, &data); if (data & SLAVE_TRST_DONE) return 0;
cond_resched();
} while (!time_after(jiffies, deadline));
return -ETIMEDOUT;
}
/** * meson_spifc_drain_buffer() - copy data from device buffer to memory * @spifc: the Meson SPI device * @buf: the destination buffer * @len: number of bytes to copy
*/ staticvoid meson_spifc_drain_buffer(struct meson_spifc *spifc, u8 *buf, int len)
{
u32 data; int i = 0;
while (i < len) {
regmap_read(spifc->regmap, REG_C0 + i, &data);
if (len - i >= 4) {
*((u32 *)buf) = data;
buf += 4;
} else {
memcpy(buf, &data, len - i); break;
}
i += 4;
}
}
/** * meson_spifc_fill_buffer() - copy data from memory to device buffer * @spifc: the Meson SPI device * @buf: the source buffer * @len: number of bytes to copy
*/ staticvoid meson_spifc_fill_buffer(struct meson_spifc *spifc, const u8 *buf, int len)
{
u32 data; int i = 0;
while (i < len) { if (len - i >= 4)
data = *(u32 *)buf; else
memcpy(&data, buf, len - i);
regmap_write(spifc->regmap, REG_C0 + i, data);
buf += 4;
i += 4;
}
}
/** * meson_spifc_setup_speed() - program the clock divider * @spifc: the Meson SPI device * @speed: desired speed in Hz
*/ staticvoid meson_spifc_setup_speed(struct meson_spifc *spifc, u32 speed)
{ unsignedlong parent, value; int n;
dev_dbg(spifc->dev, "parent %lu, speed %u, n %d\n", parent,
speed, n);
value = (n << CLOCK_DIV_SHIFT) & CLOCK_DIV_MASK;
value |= (n << CLOCK_CNT_LOW_SHIFT) & CLOCK_CNT_LOW_MASK;
value |= (((n + 1) / 2 - 1) << CLOCK_CNT_HIGH_SHIFT) &
CLOCK_CNT_HIGH_MASK;
regmap_write(spifc->regmap, REG_CLOCK, value);
}
/** * meson_spifc_txrx() - transfer a chunk of data * @spifc: the Meson SPI device * @xfer: the current SPI transfer * @offset: offset of the data to transfer * @len: length of the data to transfer * @last_xfer: whether this is the last transfer of the message * @last_chunk: whether this is the last chunk of the transfer * Return: 0 on success, a negative value on error
*/ staticint meson_spifc_txrx(struct meson_spifc *spifc, struct spi_transfer *xfer, int offset, int len, bool last_xfer, bool last_chunk)
{ bool keep_cs = true; int ret;
if (xfer->tx_buf)
meson_spifc_fill_buffer(spifc, xfer->tx_buf + offset, len);
/* clear transition done bit */
regmap_update_bits(spifc->regmap, REG_SLAVE, SLAVE_TRST_DONE, 0); /* start transfer */
regmap_update_bits(spifc->regmap, REG_CMD, CMD_USER, CMD_USER);
ret = meson_spifc_wait_ready(spifc);
if (!ret && xfer->rx_buf)
meson_spifc_drain_buffer(spifc, xfer->rx_buf + offset, len);
return ret;
}
/** * meson_spifc_transfer_one() - perform a single transfer * @host: the SPI host * @spi: the SPI device * @xfer: the current SPI transfer * Return: 0 on success, a negative value on error
*/ staticint meson_spifc_transfer_one(struct spi_controller *host, struct spi_device *spi, struct spi_transfer *xfer)
{ struct meson_spifc *spifc = spi_controller_get_devdata(host); int len, done = 0, ret = 0;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.