drivers: spi: spi-bitbang: Add support for half duplex

Implement support for half duplex communication in the bit bang SPI
driver. The SPI driver will use the MOSI pin is for both TX and RX
operations when using half duplex mode.

In half-duplex mode, the driver configures the MOSI pin as an input
pin for input only transactions. Transactions that are bidirectional
are forbidden. After an SPI transaction, the MOSI pin is left as an
input if it was an RX transaction or an output after a TX
transaction. Like before, the MOSI pin is initialized as an
(inactive) output pin when the bus is initialized.

Signed-off-by: Andreas Sandberg <andreas@sandberg.uk>
This commit is contained in:
Andreas Sandberg 2022-07-04 18:11:05 +01:00 committed by Carles Cufí
commit d33b89befe

View file

@ -27,20 +27,6 @@ struct spi_bitbang_config {
struct gpio_dt_spec miso_gpio;
};
static inline bool spi_bitbang_has_miso(const struct device *dev)
{
const struct spi_bitbang_config *info = dev->config;
return info->miso_gpio.port != NULL;
}
static inline bool spi_bitbang_has_mosi(const struct device *dev)
{
const struct spi_bitbang_config *info = dev->config;
return info->mosi_gpio.port != NULL;
}
static int spi_bitbang_configure(const struct spi_bitbang_config *info,
struct spi_bitbang_data *data,
const struct spi_config *config)
@ -88,8 +74,50 @@ static int spi_bitbang_transceive(const struct device *dev,
const struct spi_bitbang_config *info = dev->config;
struct spi_bitbang_data *data = dev->data;
struct spi_context *ctx = &data->ctx;
int rc;
const struct gpio_dt_spec *miso = NULL;
const struct gpio_dt_spec *mosi = NULL;
gpio_flags_t mosi_flags = GPIO_OUTPUT_INACTIVE;
spi_bitbang_configure(info, data, spi_cfg);
rc = spi_bitbang_configure(info, data, spi_cfg);
if (rc < 0) {
return rc;
}
if (spi_cfg->operation & SPI_HALF_DUPLEX) {
if (!info->mosi_gpio.port) {
LOG_ERR("No MOSI pin specified in half duplex mode");
return -EINVAL;
}
if (tx_bufs && rx_bufs) {
LOG_ERR("Both RX and TX specified in half duplex mode");
return -EINVAL;
} else if (tx_bufs && !rx_bufs) {
/* TX mode */
mosi = &info->mosi_gpio;
} else if (!tx_bufs && rx_bufs) {
/* RX mode */
mosi_flags = GPIO_INPUT;
miso = &info->mosi_gpio;
}
} else {
if (info->mosi_gpio.port) {
mosi = &info->mosi_gpio;
}
if (info->miso_gpio.port) {
miso = &info->miso_gpio;
}
}
if (info->mosi_gpio.port) {
rc = gpio_pin_configure_dt(&info->mosi_gpio, mosi_flags);
if (rc < 0) {
LOG_ERR("Couldn't configure MOSI pin: %d", rc);
return rc;
}
}
spi_context_buffers_setup(ctx, tx_bufs, rx_bufs, data->dfs);
@ -114,9 +142,6 @@ static int spi_bitbang_transceive(const struct device *dev,
const uint32_t wait_us = data->wait_us;
const bool has_miso = spi_bitbang_has_miso(dev);
const bool has_mosi = spi_bitbang_has_mosi(dev);
while (spi_context_tx_buf_on(ctx) || spi_context_rx_buf_on(ctx)) {
uint16_t w = 0;
@ -136,7 +161,7 @@ static int spi_bitbang_transceive(const struct device *dev,
int b = 0;
bool do_read = false;
if (has_miso && spi_context_rx_buf_on(ctx)) {
if (miso && spi_context_rx_buf_on(ctx)) {
do_read = true;
}
@ -146,8 +171,8 @@ static int spi_bitbang_transceive(const struct device *dev,
b = 0;
/* setup data out first thing */
if (has_mosi) {
gpio_pin_set_dt(&info->mosi_gpio, d);
if (mosi) {
gpio_pin_set_dt(mosi, d);
}
k_busy_wait(wait_us);
@ -156,7 +181,7 @@ static int spi_bitbang_transceive(const struct device *dev,
gpio_pin_set_dt(&info->clk_gpio, !clock_state);
if (!loop && do_read && !cpha) {
b = gpio_pin_get_dt(&info->miso_gpio);
b = gpio_pin_get_dt(miso);
}
k_busy_wait(wait_us);
@ -165,7 +190,7 @@ static int spi_bitbang_transceive(const struct device *dev,
gpio_pin_set_dt(&info->clk_gpio, clock_state);
if (!loop && do_read && cpha) {
b = gpio_pin_get_dt(&info->miso_gpio);
b = gpio_pin_get_dt(miso);
}
if (loop) {