---
# System prepended metadata

title: SPI in Linux
tags: [Linux kernel]

---

# SPI in Linux
###### tags: `Linux kernel`
{%hackmd theme-dark %}
## [Access spi in user space](https://www.kernel.org/doc/html/latest/spi/spidev.html)

[第三方的 library](https://github.com/azorg/spi)
```c
int spi_init(spi_t *self,
             const char *device,
             int mode,
             int bits,
             int speed)
{
  self->fd = open(device, O_RDWR);
  if (mode)
  {
    self->mode = (__u8) mode;
    ioctl(self->fd, SPI_IOC_WR_MODE, &self->mode);
  }
  ioctl(self->fd, SPI_IOC_RD_MODE, &self->mode);
  ioctl(self->fd, SPI_IOC_RD_LSB_FIRST, &self->lsb);
  if (bits)
  {
    self->bits = (__u8) bits;
    ioctl(self->fd, SPI_IOC_WR_BITS_PER_WORD, &self->bits);
  }
  ioctl(self->fd, SPI_IOC_RD_BITS_PER_WORD, &self->bits);
  if (speed)
  {
    self->speed = (__u32) speed;
    ioctl(self->fd, SPI_IOC_WR_MAX_SPEED_HZ, &self->speed);
  }
  ioctl(self->fd, SPI_IOC_RD_MAX_SPEED_HZ, &self->speed);
  return SPI_ERR_NONE;
}

int spi_exchange(spi_t *self, char *rx_buf, const char *tx_buf, int len)
{
  self->xfer.tx_buf = (__u64) tx_buf;
  self->xfer.rx_buf = (__u64) rx_buf;
  self->xfer.len    = (__u32) len;
  ioctl(self->fd, SPI_IOC_MESSAGE(1), &self->xfer);
  return SPI_ERR_NONE;
}
```

## Trace code in kernel
https://elixir.bootlin.com/linux/latest/source/drivers/spi/spidev.c#L345
設定最終都是呼叫到 `spi_setup`。
```c
// spidev_ioctl
case SPI_IOC_WR_MAX_SPEED_HZ: {
    get_user(tmp, (__u32 __user *)arg);
    save = spi->max_speed_hz;

    spi->max_speed_hz = tmp;
    retval = spi_setup(spi);
    pi->max_speed_hz = save;
    break;
}

int spi_setup(struct spi_device *spi)
{
	if (spi->controller->setup) {
		status = spi->controller->setup(spi);
	}
	return status;
}
```
`spi_controller` 裡面有需要各平台執行的 function pointer。`setup` 最後會呼叫到 controller 的 `setup`，是由 driver 註冊的。
```c
struct spi_device {
	struct device		dev;
	struct spi_controller	*controller;
	struct spi_controller	*master;	/* Compatibility layer */
	u32			max_speed_hz;
	// ...
};

struct spi_controller {
	struct device	dev;
	/* Setup mode and clock, etc (spi driver may call many times).
	 *
	 * IMPORTANT:  this may be called when transfers to another
	 * device are active.  DO NOT UPDATE SHARED REGISTERS in ways
	 * which could break those transfers.
	 */
	int			(*setup)(struct spi_device *spi);

	/* Bidirectional bulk transfers
	 *
	 * + The transfer() method may not sleep; its main role is
	 *   just to add the message to the queue.
	 * + For now there's no remove-from-queue operation, or
	 *   any other request management
	 * + To a given spi_device, message queueing is pure fifo
	 *
	 * + The controller's main job is to process its message queue,
	 *   selecting a chip (for masters), then transferring data
	 * + If there are multiple spi_device children, the i/o queue
	 *   arbitration algorithm is unspecified (round robin, fifo,
	 *   priority, reservations, preemption, etc)
	 *
	 * + Chipselect stays active during the entire message
	 *   (unless modified by spi_transfer.cs_change != 0).
	 * + The message transfers use clock and SPI mode parameters
	 *   previously established by setup() for this device
	 */
	int			(*transfer)(struct spi_device *spi,
						struct spi_message *mesg);
};
```
以 RP4 為例，要在樹莓派 fork 的 linux 版本中才找的到對應的 driver

https://github.com/raspberrypi/linux/blob/rpi-3.6.y/drivers/spi/spi-bcm2708.c
```c
tatic int __devinit bcm2708_spi_probe(struct platform_device *pdev)
{
	INIT_WORK(&bs->work, bcm2708_work);
	// ...
	master->setup = bcm2708_spi_setup;
	master->transfer = bcm2708_spi_transfer;
}

static int bcm2708_spi_transfer(struct spi_device *spi, struct spi_message *msg)
{
	queue_work(bs->workq, &bs->work);
}
```
可以看到 `setup` 是直接執行，而 `transfer` 則是交給 scheduler 去安排，`bcm2708_work` 才會是真的執行的地方。