2

I'm an embedded software engineer who is not familiar with the linux driver model, but familiar with device drivers in general.

I want to do something simple, namely, wiggling a couple GPIOs from within the SPI driver as individual bytes go out.

In general SPI is accessed through a simple interface to configure the device, read bytes, and write bytes. I'm having trouble "seeing" that hidden in all of this "other stuff" in spi-bcm2708.c.

This is a hack that I'm not interested in spending a ton of time on, so proposing hacky solutions is definitely OK. My plan is to simply locate where this driver is actually communicating bytes to the SPI peripheral and wrap it with my direct GPIO wiggles. These wiggles will be accomplished through an ioremap'd pointer to the GPIO peripheral.

I'm hoping that someone can point me to where this driver is actually writing bytes out to the peripheral. Perhaps its all DMA driven and synchronizing SPI byte transfers with my gpio wiggles will be impossible.

  • Welcome to the Pi Exchange. This is surely a very interesting question but more low level than the average question on this site. It would be interesting to see any answers. – Piotr Kula May 12 '15 at 21:06
2

The current Rapsberry Pi Linux driver does not use DMA.

The SPI peripheral has input and output FIFOS. The bytes to be transferred are written to the output FIFO and a transfer is triggered.

BCM2835 ARM Peripherals page 148.

You don't really have any visibility of when each byte is transferred, certainly you won't be able to synchronise the writing of gpios against individual bytes. To do that you will have to bit bang SPI.

I include the source code my pigpio library uses to write to the main SPI peripheral. It's more complicated than the official driver as it also includes support for 3-wire operation.

static void spiGoS(
   unsigned speed,
   uint32_t flags,
   char     *txBuf,
   char     *rxBuf,
   unsigned count)
{
   unsigned txCnt=0;
   unsigned rxCnt=0;
   unsigned cnt, cnt4w, cnt3w;
   uint32_t spiDefaults;
   unsigned mode, channel, cspol, cspols, flag3w, ren3w;

   channel = PI_SPI_FLAGS_GET_CHANNEL(flags);
   mode   =  PI_SPI_FLAGS_GET_MODE   (flags);
   cspols =  PI_SPI_FLAGS_GET_CSPOLS(flags);
   cspol  =  (cspols>>channel) & 1;
   flag3w =  PI_SPI_FLAGS_GET_3WIRE(flags);
   ren3w =   PI_SPI_FLAGS_GET_3WREN(flags);

   spiDefaults = SPI_CS_MODE(mode)     |
                 SPI_CS_CSPOLS(cspols) |
                 SPI_CS_CS(channel)    |
                 SPI_CS_CSPOL(cspol)   |
                 SPI_CS_CLEAR(3);

   spiReg[SPI_CS] = spiDefaults; /* stop */

   if (!count) return;

   if (flag3w)
   {
      if (ren3w < count)
      {
         cnt4w = ren3w;
         cnt3w = count - ren3w;
      }
      else
      {
         cnt4w = count;
         cnt3w = 0;
      }
   }
   else
   {
      cnt4w = count;
      cnt3w = 0;
   }

   spiReg[SPI_CLK] = 250000000/speed;

   spiReg[SPI_CS] = spiDefaults | SPI_CS_TA; /* start */

   cnt = cnt4w;

   while((txCnt < cnt) || (rxCnt < cnt))
   {
      while((rxCnt < cnt) && ((spiReg[SPI_CS] & SPI_CS_RXD)))
      {
         if (rxBuf) rxBuf[rxCnt] = spiReg[SPI_FIFO];
         else       spi_dummy    = spiReg[SPI_FIFO];
         rxCnt++;
      }

      while((txCnt < cnt) && ((spiReg[SPI_CS] & SPI_CS_TXD)))
      {
         if (txBuf) spiReg[SPI_FIFO] = txBuf[txCnt];
         else       spiReg[SPI_FIFO] = 0;
         txCnt++;
      }
   }

   while (!(spiReg[SPI_CS] & SPI_CS_DONE)) ;

   /* now switch to 3-wire bus */

   cnt += cnt3w;

   spiReg[SPI_CS] |= SPI_CS_REN;

   while((txCnt < cnt) || (rxCnt < cnt))
   {
      while((rxCnt < cnt) && ((spiReg[SPI_CS] & SPI_CS_RXD)))
      {
         if (rxBuf) rxBuf[rxCnt] = spiReg[SPI_FIFO];
         else       spi_dummy    = spiReg[SPI_FIFO];
         rxCnt++;
      }

      while((txCnt < cnt) && ((spiReg[SPI_CS] & SPI_CS_TXD)))
      {
         if (txBuf) spiReg[SPI_FIFO] = txBuf[txCnt];
         else       spiReg[SPI_FIFO] = 0;
         txCnt++;
      }
   }

   while (!(spiReg[SPI_CS] & SPI_CS_DONE)) ;

   spiReg[SPI_CS] = spiDefaults; /* stop */
}
  • This seems like a good start, but I don't think they would need to resort to bit banging. Controlling the slave selects as GPIO should let multibyte transfers be programmed as a series of single byte transfers via the SPI engine, allowing arbitrary operations to be performed before loading each byte into the FIFO. Or it may be possible to disable the FIFOs. – Chris Stratton May 13 '15 at 9:08
  • As is often the case the devil is in the detail. Assuming all SPI traffic is going through the official SPI driver it might be possible to take control of the slave select gpios and fill the fifo with one byte at a time. Monitoring for completion and toggling needed gpios when the fifo is empty. However, in my opinion, it may be simpler just to bit bang SPI. Without knowing details of what the OP is trying to achieve it is speculation. – joan May 13 '15 at 9:22

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.