/* * This buffer will be used mainly for read/write operations. Since * they're quite large, we cannot use the stack. Protection is not * needed because all SPI communication is serialized by the SPI core.
*/ void *buf;
u8 bpw;
u32 speed;
u16 mode;
u8 cs;
};
/* * Enable/Disable SPI module. The disable command will wait for transfers to * complete first.
*/ staticint dln2_spi_enable(struct dln2_spi *dln2, bool enable)
{
u16 cmd; struct {
u8 port;
u8 wait_for_completion;
} tx; unsigned len = sizeof(tx);
tx.port = dln2->port;
if (enable) {
cmd = DLN2_SPI_ENABLE;
len -= sizeof(tx.wait_for_completion);
} else {
tx.wait_for_completion = DLN2_TRANSFERS_WAIT_COMPLETE;
cmd = DLN2_SPI_DISABLE;
}
/* * Select/unselect multiple CS lines. The selected lines will be automatically * toggled LOW/HIGH by the board firmware during transfers, provided they're * enabled first. * * Ex: cs_mask = 0x03 -> CS0 & CS1 will be selected and the next WR/RD operation * will toggle the lines LOW/HIGH automatically.
*/ staticint dln2_spi_cs_set(struct dln2_spi *dln2, u8 cs_mask)
{ struct {
u8 port;
u8 cs;
} tx;
tx.port = dln2->port;
/* * According to Diolan docs, "a slave device can be selected by changing * the corresponding bit value to 0". The rest must be set to 1. Hence * the bitwise NOT in front.
*/
tx.cs = ~cs_mask;
/* * Select one CS line. The other lines will be un-selected.
*/ staticint dln2_spi_cs_set_one(struct dln2_spi *dln2, u8 cs)
{ return dln2_spi_cs_set(dln2, BIT(cs));
}
/* * Enable/disable CS lines for usage. The module has to be disabled first.
*/ staticint dln2_spi_cs_enable(struct dln2_spi *dln2, u8 cs_mask, bool enable)
{ struct {
u8 port;
u8 cs;
} tx;
u16 cmd;
/* * Set the bus speed. The module will automatically round down to the closest * available frequency and returns it. The module has to be disabled first.
*/ staticint dln2_spi_set_speed(struct dln2_spi *dln2, u32 speed)
{ int ret; struct {
u8 port;
__le32 speed;
} __packed tx; struct {
__le32 speed;
} rx; int rx_len = sizeof(rx);
/* * Copy the data to DLN2 buffer and change the byte order to LE, requested by * DLN2 module. SPI core makes sure that the data length is a multiple of word * size.
*/ staticint dln2_spi_copy_to_buf(u8 *dln2_buf, const u8 *src, u16 len, u8 bpw)
{ #ifdef __LITTLE_ENDIAN
memcpy(dln2_buf, src, len); #else if (bpw <= 8) {
memcpy(dln2_buf, src, len);
} elseif (bpw <= 16) {
__le16 *d = (__le16 *)dln2_buf;
u16 *s = (u16 *)src;
len = len / 2; while (len--)
*d++ = cpu_to_le16p(s++);
} else {
__le32 *d = (__le32 *)dln2_buf;
u32 *s = (u32 *)src;
len = len / 4; while (len--)
*d++ = cpu_to_le32p(s++);
} #endif
return 0;
}
/* * Copy the data from DLN2 buffer and convert to CPU byte order since the DLN2 * buffer is LE ordered. SPI core makes sure that the data length is a multiple * of word size. The RX dln2_buf is 2 byte aligned so, for BE, we have to make * sure we avoid unaligned accesses for 32 bit case.
*/ staticint dln2_spi_copy_from_buf(u8 *dest, const u8 *dln2_buf, u16 len, u8 bpw)
{ #ifdef __LITTLE_ENDIAN
memcpy(dest, dln2_buf, len); #else if (bpw <= 8) {
memcpy(dest, dln2_buf, len);
} elseif (bpw <= 16) {
u16 *d = (u16 *)dest;
__le16 *s = (__le16 *)dln2_buf;
len = len / 2; while (len--)
*d++ = le16_to_cpup(s++);
} else {
u32 *d = (u32 *)dest;
__le32 *s = (__le32 *)dln2_buf;
len = len / 4; while (len--)
*d++ = get_unaligned_le32(s++);
} #endif
if (data_len > DLN2_SPI_MAX_XFER_SIZE) return -EINVAL;
/* * Since this is a pseudo full-duplex communication, we're perfectly * safe to use the same buffer for both tx and rx. When DLN2 sends the * response back, with the rx data, we don't need the tx buffer anymore.
*/
tx = dln2->buf;
rx = dln2->buf;
host = spi_alloc_host(&pdev->dev, sizeof(*dln2)); if (!host) return -ENOMEM;
device_set_node(&host->dev, dev_fwnode(dev));
platform_set_drvdata(pdev, host);
dln2 = spi_controller_get_devdata(host);
dln2->buf = devm_kmalloc(&pdev->dev, DLN2_SPI_BUF_SIZE, GFP_KERNEL); if (!dln2->buf) {
ret = -ENOMEM; goto exit_free_host;
}
dln2->host = host;
dln2->pdev = pdev;
dln2->port = pdata->port; /* cs/mode can never be 0xff, so the first transfer will set them */
dln2->cs = 0xff;
dln2->mode = 0xff;
/* disable SPI module before continuing with the setup */
ret = dln2_spi_enable(dln2, false); if (ret < 0) {
dev_err(&pdev->dev, "Failed to disable SPI module\n"); goto exit_free_host;
}
ret = dln2_spi_get_cs_num(dln2, &host->num_chipselect); if (ret < 0) {
dev_err(&pdev->dev, "Failed to get number of CS pins\n"); goto exit_free_host;
}
ret = dln2_spi_get_speed_range(dln2,
&host->min_speed_hz,
&host->max_speed_hz); if (ret < 0) {
dev_err(&pdev->dev, "Failed to read bus min/max freqs\n"); goto exit_free_host;
}
ret = dln2_spi_get_supported_frame_sizes(dln2,
&host->bits_per_word_mask); if (ret < 0) {
dev_err(&pdev->dev, "Failed to read supported frame sizes\n"); goto exit_free_host;
}
ret = dln2_spi_cs_enable_all(dln2, true); if (ret < 0) {
dev_err(&pdev->dev, "Failed to enable CS pins\n"); goto exit_free_host;
}
/* enable SPI module, we're good to go */
ret = dln2_spi_enable(dln2, true); if (ret < 0) {
dev_err(&pdev->dev, "Failed to enable SPI module\n"); goto exit_free_host;
}
ret = spi_controller_suspend(host); if (ret < 0) return ret;
if (!pm_runtime_suspended(dev)) {
ret = dln2_spi_enable(dln2, false); if (ret < 0) return ret;
}
/* * USB power may be cut off during sleep. Resetting the following * parameters will force the board to be set up before first transfer.
*/
dln2->cs = 0xff;
dln2->speed = 0;
dln2->bpw = 0;
dln2->mode = 0xff;
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.