Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:24:05

0001 /* SPDX-License-Identifier: BSD-2-Clause */
0002 
0003 /**
0004  * @file
0005  *
0006  * @ingroup RTEMSDeviceSPIGPIO
0007  *
0008  * @brief This file provides the implementation of @ref RTEMSDeviceSPIGPIO.
0009  */
0010 
0011 /*
0012  * Copyright (C) 2024 embedded brains GmbH & Co. KG
0013  *
0014  * Redistribution and use in source and binary forms, with or without
0015  * modification, are permitted provided that the following conditions
0016  * are met:
0017  * 1. Redistributions of source code must retain the above copyright
0018  *    notice, this list of conditions and the following disclaimer.
0019  * 2. Redistributions in binary form must reproduce the above copyright
0020  *    notice, this list of conditions and the following disclaimer in the
0021  *    documentation and/or other materials provided with the distribution.
0022  *
0023  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0024  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0025  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0026  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
0027  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
0028  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
0029  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
0030  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
0031  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
0032  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
0033  * POSSIBILITY OF SUCH DAMAGE.
0034  */
0035 
0036 #include <bsp.h>
0037 #include <dev/spi/spi.h>
0038 #include <dev/spi/spi-gpio.h>
0039 
0040 struct spi_gpio_bus {
0041   spi_bus base;
0042   const struct spi_gpio_params *p;
0043 };
0044 
0045 static int spi_gpio_check_msg(
0046   struct spi_gpio_bus *bus,
0047   const spi_ioc_transfer *msg
0048 )
0049 {
0050   /*
0051    * Check that only implemented modes are requested.
0052    *
0053    * Ignore the frequency. This driver doesn't give any guarantees regarding
0054    * that.
0055    */
0056   if ((msg->mode & ~(SPI_CPHA | SPI_CPOL | SPI_NO_CS)) != 0 ||
0057       (msg->bits_per_word != 8) ||
0058       (msg->delay_usecs != 0) ||
0059       (msg->cs >= SPI_GPIO_MAX_CS) ||
0060       (bus->p->set_cs[msg->cs] == NULL)) {
0061     return -EINVAL;
0062   }
0063 
0064   return 0;
0065 }
0066 
0067 static int spi_gpio_transfer_msg(
0068   struct spi_gpio_bus *bus,
0069   const spi_ioc_transfer *msg
0070 )
0071 {
0072   uint8_t cs = msg->cs;
0073   bool clk_idle = ((msg->mode & SPI_CPOL) != 0);
0074   bool cpha = ((msg->mode & SPI_CPHA) != 0);
0075   const uint8_t *tx_buf = msg->tx_buf;
0076   uint8_t *rx_buf = msg->rx_buf;
0077   size_t len = msg->len;
0078   int i;
0079   int rv;
0080 
0081   rv = spi_gpio_check_msg(bus, msg);
0082   if (rv != 0) {
0083     return rv;
0084   }
0085 
0086   /*
0087    * Make sure that all chip selects but the one in the message are idle.
0088    * Excluding the one in the message is necessary to avoid deselecting the chip
0089    * between messages.
0090    */
0091   for (i = 0; i < SPI_GPIO_MAX_CS; ++i) {
0092     if (i != cs && bus->p->set_cs[i] != NULL) {
0093       bus->p->set_cs[i](bus->p->set_cs_arg[i], bus->p->cs_idle[i]);
0094     }
0095   }
0096 
0097   /* Set up clock according to CPOL. */
0098   bus->p->set_clk(bus->p->set_clk_arg, clk_idle);
0099 
0100   /* Now select the chip (if it isn't selected from the previous message) */
0101   bus->p->set_cs[cs](bus->p->set_cs_arg[cs], !bus->p->cs_idle[cs]);
0102 
0103   while (len > 0) {
0104     uint8_t out = 0xff;
0105     uint8_t in = 0;
0106     size_t ctr;
0107 
0108     --len;
0109 
0110     if (tx_buf != NULL) {
0111       out = *tx_buf;
0112       ++tx_buf;
0113     }
0114 
0115     /*
0116      * Read / Write the data.
0117      *
0118      * If CPHA=0: Set up data. Read data. Clock to active. Clock to inactive.
0119      * If CPHA=1: Clock to active. Set up data. Read data. Clock to inactive.
0120      */
0121     for (ctr = 0; ctr < sizeof(out) * 8; ++ctr) {
0122       bool d_out;
0123       bool d_in;
0124 
0125       if (cpha) {
0126         bus->p->set_clk(bus->p->set_clk_arg, !clk_idle);
0127       }
0128 
0129       d_out = ((out & 0x80) != 0);
0130       out <<= 1;
0131       bus->p->set_mosi(bus->p->set_mosi_arg, d_out);
0132 
0133       d_in = bus->p->get_miso(bus->p->get_miso_arg);
0134       in = (in << 1) | (d_in ? 1 : 0);
0135 
0136       if (!cpha) {
0137         bus->p->set_clk(bus->p->set_clk_arg, !clk_idle);
0138       }
0139       bus->p->set_clk(bus->p->set_clk_arg, clk_idle);
0140     }
0141 
0142     if (rx_buf != NULL) {
0143       *rx_buf = in;
0144       ++rx_buf;
0145     }
0146   }
0147 
0148   /* Deselect the chip if the message wants that. */
0149   if (msg->cs_change) {
0150     bus->p->set_cs[cs](bus->p->set_cs_arg[cs], bus->p->cs_idle[cs]);
0151   }
0152 
0153   return 0;
0154 }
0155 
0156 static int spi_gpio_transfer(
0157   spi_bus *base,
0158   const spi_ioc_transfer *msgs,
0159   uint32_t n
0160 )
0161 {
0162   struct spi_gpio_bus *bus;
0163   bus = (struct spi_gpio_bus *) base;
0164   int rv = 0;
0165 
0166   while (n > 0 && rv == 0) {
0167     rv = spi_gpio_transfer_msg(bus, msgs);
0168     --n;
0169     ++msgs;
0170   };
0171 
0172   return rv;
0173 }
0174 
0175 static void spi_gpio_destroy(spi_bus *base)
0176 {
0177   struct spi_gpio_bus *bus;
0178   bus = (struct spi_gpio_bus *) base;
0179   spi_bus_destroy_and_free(&bus->base);
0180 }
0181 
0182 static int spi_gpio_setup(spi_bus *base)
0183 {
0184   struct spi_gpio_bus *bus;
0185   bus = (struct spi_gpio_bus *) base;
0186   struct spi_ioc_transfer msg = {
0187     .speed_hz = base->speed_hz,
0188     .delay_usecs = base->delay_usecs,
0189     .bits_per_word = base->bits_per_word,
0190     .mode = base->mode,
0191     .cs = base->cs,
0192   };
0193 
0194   return spi_gpio_check_msg(bus, &msg);
0195 }
0196 
0197 rtems_status_code
0198 spi_gpio_init(const char* device, const struct spi_gpio_params *params)
0199 {
0200   struct spi_gpio_bus *bus;
0201   int eno;
0202 
0203   if (device == NULL || params == NULL) {
0204     return RTEMS_INVALID_ADDRESS;
0205   }
0206 
0207   bus = (struct spi_gpio_bus*) spi_bus_alloc_and_init(sizeof(*bus));
0208   if (bus == NULL) {
0209     return RTEMS_UNSATISFIED;
0210   }
0211 
0212   bus->p = params;
0213 
0214   bus->base.transfer = spi_gpio_transfer;
0215   bus->base.destroy = spi_gpio_destroy;
0216   bus->base.setup = spi_gpio_setup;
0217 
0218   eno = spi_bus_register(&bus->base, device);
0219   if (eno != 0) {
0220     spi_bus_destroy_and_free(&bus->base);
0221     return RTEMS_UNSATISFIED;
0222   }
0223 
0224   return RTEMS_SUCCESSFUL;
0225 }