text: /*
   * Copyright (c) 2017 Google LLC.
   *
   * SPDX-License-Identifier: Apache-2.0
   */
  #define DT_DRV_COMPAT atmel_sam0_spi
  
  #define LOG_LEVEL CONFIG_SPI_LOG_LEVEL
  #include <logging/log.h>
  LOG_MODULE_REGISTER(spi_sam0);
  
  #include "spi_context.h"
  #include <errno.h>
  #include <device.h>
  #include <drivers/spi.h>
  #include <soc.h>
  #include <drivers/dma.h>
  
  #ifndef SERCOM_SPI_CTRLA_MODE_SPI_MASTER_Val
  #define SERCOM_SPI_CTRLA_MODE_SPI_MASTER_Val (0x3)
  #endif
  
  /* Device constant configuration parameters */
  struct spi_sam0_config {
  	SercomSpi *regs;
  	u32_t pads;
  #ifdef MCLK
  	volatile u32_t *mclk;
  	u32_t mclk_mask;
  	u16_t gclk_core_id;
  #else
  	u32_t pm_apbcmask;
  	u16_t gclk_clkctrl_id;
  #endif
  #ifdef CONFIG_SPI_ASYNC
  	u8_t tx_dma_request;
  	u8_t tx_dma_channel;
  	u8_t rx_dma_request;
  	u8_t rx_dma_channel;
  #endif
  };
  
  /* Device run time data */
  struct spi_sam0_data {
  	struct spi_context ctx;
  #ifdef CONFIG_SPI_ASYNC
  	struct device *dma;
  	u32_t dma_segment_len;
  #endif
  };
  
  static void wait_synchronization(SercomSpi *regs)
  {
  #if defined(SERCOM_SPI_SYNCBUSY_MASK)
  	/* SYNCBUSY is a register */
  	while ((regs->SYNCBUSY.reg & SERCOM_SPI_SYNCBUSY_MASK) != 0) {
  	}
  #elif defined(SERCOM_SPI_STATUS_SYNCBUSY)
  	/* SYNCBUSY is a bit */
  	while ((regs->STATUS.reg & SERCOM_SPI_STATUS_SYNCBUSY) != 0) {
  	}
  #else
  #error Unsupported device
  #endif
  }
  
  static int spi_sam0_configure(struct device *dev,
  			      const struct spi_config *config)
  {
  	const struct spi_sam0_config *cfg = dev->config->config_info;
  	struct spi_sam0_data *data = dev->driver_data;
  	SercomSpi *regs = cfg->regs;
  	SERCOM_SPI_CTRLA_Type ctrla = {.reg = 0};
  	SERCOM_SPI_CTRLB_Type ctrlb = {.reg = 0};
  	int div;
  
  	if (spi_context_configured(&data->ctx, config)) {
  		return 0;
  	}
  
  	if (SPI_OP_MODE_GET(config->operation) != SPI_OP_MODE_MASTER) {
  		/* Slave mode is not implemented. */
  		return -ENOTSUP;
  	}
  
  	ctrla.bit.MODE = SERCOM_SPI_CTRLA_MODE_SPI_MASTER_Val;
  
  	if ((config->operation & SPI_TRANSFER_LSB) != 0U) {
  		ctrla.bit.DORD = 1;
  	}
  
  	if ((config->operation & SPI_MODE_CPOL) != 0U) {
  		ctrla.bit.CPOL = 1;
  	}
  
  	if ((config->operation & SPI_MODE_CPHA) != 0U) {
  		ctrla.bit.CPHA = 1;
  	}
  
  	ctrla.reg |= cfg->pads;
  
  	if ((config->operation & SPI_MODE_LOOP) != 0U) {
  		/* Put MISO and MOSI on the same pad */
  		ctrla.bit.DOPO = 0;
  		ctrla.bit.DIPO = 0;
  	}
  
  	ctrla.bit.ENABLE = 1;
  	ctrlb.bit.RXEN = 1;
  
  	if (SPI_WORD_SIZE_GET(config->operation) != 8) {
  		return -ENOTSUP;
  	}
  
  	/* 8 bits per transfer */
  	ctrlb.bit.CHSIZE = 0;
  
  	/* Use the requested or next highest possible frequency */
  	div = (SOC_ATMEL_SAM0_GCLK0_FREQ_HZ / config->frequency) / 2U - 1;
  	div = MAX(0, MIN(UINT8_MAX, div));
  
  	/* Update the configuration only if it has changed */
  	if (regs->CTRLA.reg != ctrla.reg || regs->CTRLB.reg != ctrlb.reg ||
  	    regs->BAUD.reg != div) {
  		regs->CTRLA.bit.ENABLE = 0;
  		wait_synchronization(regs);
  
  		regs->CTRLB = ctrlb;
  		wait_synchronization(regs);
  		regs->BAUD.reg = div;
  		wait_synchronization(regs);
  		regs->CTRLA = ctrla;
  		wait_synchronization(regs);
  	}
  
  	data->ctx.config = config;
  	spi_context_cs_configure(&data->ctx);
  
  	return 0;
  }
  
  static bool spi_sam0_transfer_ongoing(struct spi_sam0_data *data)
  {
  	return spi_context_tx_on(&data->ctx) || spi_context_rx_on(&data->ctx);
  }
  
  static void spi_sam0_shift_master(SercomSpi *regs, struct spi_sam0_data *data)
  {
  	u8_t tx;
  	u8_t rx;
  
  	if (spi_context_tx_buf_on(&data->ctx)) {
  		tx = *(u8_t *)(data->ctx.tx_buf);
  	} else {
  		tx = 0U;
  	}
  
  	while (!regs->INTFLAG.bit.DRE) {
  	}
  
  	regs->DATA.reg = tx;
  	spi_context_update_tx(&data->ctx, 1, 1);
  
  	while (!regs->INTFLAG.bit.RXC) {
  	}
  
  	rx = regs->DATA.reg;
  
  	if (spi_context_rx_buf_on(&data->ctx)) {
  		*data->ctx.rx_buf = rx;
  	}
  	spi_context_update_rx(&data->ctx, 1, 1);
  }
  
  /* Finish any ongoing writes and drop any remaining read data */
  static void spi_sam0_finish(SercomSpi *regs)
  {
  	while (!regs->INTFLAG.bit.TXC) {
  	}
  
  	while (regs->INTFLAG.bit.RXC) {
  		(void)regs->DATA.reg;
  	}
  }
  
  /* Fast path that transmits a buf */
  static void spi_sam0_fast_tx(SercomSpi *regs, const struct spi_buf *tx_buf)
  {
  	const u8_t *p = tx_buf->buf;
  	const u8_t *pend = (u8_t *)tx_buf->buf + tx_buf->len;
  	u8_t ch;
  
  	while (p != pend) {
  		ch = *p++;
  
  		while (!regs->INTFLAG.bit.DRE) {
  		}
  
  		regs->DATA.reg = ch;
  	}
  
  	spi_sam0_finish(regs);
  }
  
  /* Fast path that reads into a buf */
  static void spi_sam0_fast_rx(SercomSpi *regs, const struct spi_buf *rx_buf)
  {
  	u8_t *rx = rx_buf->buf;
  	int len = rx_buf->len;
  
  	if (len <= 0) {
  		return;
  	}
  
  	/* See the comment in spi_sam0_fast_txrx re: interleaving. */
  
  	/* Write the first byte */
  	regs->DATA.reg = 0;
  	len--;
  
  	while (len) {
  		/* Load byte N+1 into the transmit register */
  		regs->DATA.reg = 0;
  		len--;
  
  		/* Read byte N+0 from the receive register */
  		while (!regs->INTFLAG.bit.RXC) {
  		}
  
  		*rx++ = regs->DATA.reg;
  	}
  
  	/* Read the final incoming byte */
  	while (!regs->INTFLAG.bit.RXC) {
  	}
  
  	*rx = regs->DATA.reg;
  
  	spi_sam0_finish(regs);
  }
  
  /* Fast path that writes and reads bufs of the same length */
  static void spi_sam0_fast_txrx(SercomSpi *regs,
  			       const struct spi_buf *tx_buf,
  			       const struct spi_buf *rx_buf)
  {
  	const u8_t *tx = tx_buf->buf;
  	const u8_t *txend = (u8_t *)tx_buf->buf + tx_buf->len;
  	u8_t *rx = rx_buf->buf;
  	size_t len = rx_buf->len;
  
  	if (len == 0) {
  		return;
  	}
  
  	/*
  	 * The code below interleaves the transmit writes with the
  	 * receive reads to keep the bus fully utilised.  The code is
  	 * equivalent to:
  	 *
  	 * Transmit byte 0
  	 * Loop:
  	 * - Transmit byte n+1
  	 * - Receive byte n
  	 * Receive the final byte
  	 */
  
  	/* Write the first byte */
  	regs->DATA.reg = *tx++;
  
  	while (tx != txend) {
  		/* Load byte N+1 into the transmit register.  TX is
  		 * single buffered and we have at most one byte in
  		 * flight so skip the DRE check.
  		 */
  		regs->DATA.reg = *tx++;
  
  		/* Read byte N+0 from the receive register */
  		while (!regs->INTFLAG.bit.RXC) {
  		}
  
  		*rx++ = regs->DATA.reg;
  	}
  
  	/* Read the final incoming byte */
  	while (!regs->INTFLAG.bit.RXC) {
  	}
  
  	*rx = regs->DATA.reg;
  
  	spi_sam0_finish(regs);
  }
  
  /* Fast path where every overlapping tx and rx buffer is the same length */
  static void spi_sam0_fast_transceive(struct device *dev,
  				     const struct spi_config *config,
  				     const struct spi_buf_set *tx_bufs,
  				     const struct spi_buf_set *rx_bufs)
  {
  	const struct spi_sam0_config *cfg = dev->config->config_info;
  	size_t tx_count = 0;
  	size_t rx_count = 0;
  	SercomSpi *regs = cfg->regs;
  	const struct spi_buf *tx = NULL;
  	const struct spi_buf *rx = NULL;
  
  	if (tx_bufs) {
  		tx = tx_bufs->buffers;
  		tx_count = tx_bufs->count;
  	}
  
  	if (rx_bufs) {
  		rx = rx_bufs->buffers;
  		rx_count = rx_bufs->count;
  	} else {
  		rx = NULL;
  	}
  
  	while (tx_count != 0 && rx_count != 0) {
  		if (tx->buf == NULL) {
  			spi_sam0_fast_rx(regs, rx);
  		} else if (rx->buf == NULL) {
  			spi_sam0_fast_tx(regs, tx);
  		} else {
  			spi_sam0_fast_txrx(regs, tx, rx);
  		}
  
  		tx++;
  		tx_count--;
  		rx++;
  		rx_count--;
  	}
  
  	for (; tx_count != 0; tx_count--) {
  		spi_sam0_fast_tx(regs, tx++);
  	}
  
  	for (; rx_count != 0; rx_count--) {
  		spi_sam0_fast_rx(regs, rx++);
  	}
  }
  
  /* Returns true if the request is suitable for the fast
   * path. Specifically, the bufs are a sequence of:
   *
   * - Zero or more RX and TX buf pairs where each is the same length.
   * - Zero or more trailing RX only bufs
   * - Zero or more trailing TX only bufs
   */
  static bool spi_sam0_is_regular(const struct spi_buf_set *tx_bufs,
  				const struct spi_buf_set *rx_bufs)
  {
  	const struct spi_buf *tx = NULL;
  	const struct spi_buf *rx = NULL;
  	size_t tx_count = 0;
  	size_t rx_count = 0;
  
  	if (tx_bufs) {
  		tx = tx_bufs->buffers;
  		tx_count = tx_bufs->count;
  	}
  
  	if (rx_bufs) {
  		rx = rx_bufs->buffers;
  		rx_count = rx_bufs->count;
  	}
  
  	if (!tx || !rx) {
  		return false;
  	}
  
  	while (tx_count != 0 && rx_count != 0) {
  		if (tx->len != rx->len) {
  			return false;
  		}
  
  		tx++;
  		tx_count--;
  		rx++;
  		rx_count--;
  	}
  
  	return true;
  }
  
  static int spi_sam0_transceive(struct device *dev,
  			       const struct spi_config *config,
  			       const struct spi_buf_set *tx_bufs,
  			       const struct spi_buf_set *rx_bufs)
  {
  	const struct spi_sam0_config *cfg = dev->config->config_info;
  	struct spi_sam0_data *data = dev->driver_data;
  	SercomSpi *regs = cfg->regs;
  	int err;
  
  	spi_context_lock(&data->ctx, false, NULL);
  
  	err = spi_sam0_configure(dev, config);
  	if (err != 0) {
  		goto done;
  	}
  
  	spi_context_cs_control(&data->ctx, true);
  
  	/* This driver special cases the common send only, receive
  	 * only, and transmit then receive operations.	This special
  	 * casing is 4x faster than the spi_context() routines
  	 * and allows the transmit and receive to be interleaved.
  	 */
  	if (spi_sam0_is_regular(tx_bufs, rx_bufs)) {
  		spi_sam0_fast_transceive(dev, config, tx_bufs, rx_bufs);
  	} else {
  		spi_context_buffers_setup(&data->ctx, tx_bufs, rx_bufs, 1);
  
  		do {
  			spi_sam0_shift_master(regs, data);
  		} while (spi_sam0_transfer_ongoing(data));
  	}
  
  	spi_context_cs_control(&data->ctx, false);
  
  done:
  	spi_context_release(&data->ctx, err);
  	return err;
  }
  
  static int spi_sam0_transceive_sync(struct device *dev,
  				    const struct spi_config *config,
  				    const struct spi_buf_set *tx_bufs,
  				    const struct spi_buf_set *rx_bufs)
  {
  	return spi_sam0_transceive(dev, config, tx_bufs, rx_bufs);
  }
  
  #ifdef CONFIG_SPI_ASYNC
  
  static void spi_sam0_dma_rx_done(void *arg, u32_t id, int error_code);
  
  static int spi_sam0_dma_rx_load(struct device *dev, u8_t *buf,
  				size_t len)
  {
  	const struct spi_sam0_config *cfg = dev->config->config_info;
  	struct spi_sam0_data *data = dev->driver_data;
  	SercomSpi *regs = cfg->regs;
  	struct dma_config dma_cfg = { 0 };
  	struct dma_block_config dma_blk = { 0 };
  	int retval;
  
  	dma_cfg.channel_direction = PERIPHERAL_TO_MEMORY;
  	dma_cfg.source_data_size = 1;
  	dma_cfg.dest_data_size = 1;
  	dma_cfg.callback_arg = dev;
  	dma_cfg.dma_callback = spi_sam0_dma_rx_done;
  	dma_cfg.block_count = 1;
  	dma_cfg.head_block = &dma_blk;
  	dma_cfg.dma_slot = cfg->rx_dma_request;
  
  	dma_blk.block_size = len;
  
  	if (buf != NULL) {
  		dma_blk.dest_address = (u32_t)buf;
  	} else {
  		static u8_t dummy;
  
  		dma_blk.dest_address = (u32_t)&dummy;
  		dma_blk.dest_addr_adj = DMA_ADDR_ADJ_NO_CHANGE;
  	}
  
  	dma_blk.source_address = (u32_t)(&(regs->DATA.reg));
  	dma_blk.source_addr_adj = DMA_ADDR_ADJ_NO_CHANGE;
  
  	retval = dma_config(data->dma, cfg->rx_dma_channel,
  			    &dma_cfg);
  	if (retval != 0) {
  		return retval;
  	}
  
  	return dma_start(data->dma, cfg->rx_dma_channel);
  }
  
  static int spi_sam0_dma_tx_load(struct device *dev, const u8_t *buf,
  				size_t len)
  {
  	const struct spi_sam0_config *cfg = dev->config->config_info;
  	struct spi_sam0_data *data = dev->driver_data;
  	SercomSpi *regs = cfg->regs;
  	struct dma_config dma_cfg = { 0 };
  	struct dma_block_config dma_blk = { 0 };
  	int retval;
  
  	dma_cfg.channel_direction = PERIPHERAL_TO_MEMORY;
  	dma_cfg.source_data_size = 1;
  	dma_cfg.dest_data_size = 1;
  	dma_cfg.block_count = 1;
  	dma_cfg.head_block = &dma_blk;
  	dma_cfg.dma_slot = cfg->tx_dma_request;
  
  	dma_blk.block_size = len;
  
  	if (buf != NULL) {
  		dma_blk.source_address = (u32_t)buf;
  	} else {
  		static const u8_t dummy;
  
  		dma_blk.source_address = (u32_t)&dummy;
  		dma_blk.source_addr_adj = DMA_ADDR_ADJ_NO_CHANGE;
  	}
  
  	dma_blk.dest_address = (u32_t)(&(regs->DATA.reg));
  	dma_blk.dest_addr_adj = DMA_ADDR_ADJ_NO_CHANGE;
  
  	retval = dma_config(data->dma, cfg->tx_dma_channel,
  			    &dma_cfg);
  
  	if (retval != 0) {
  		return retval;
  	}
  
  	return dma_start(data->dma, cfg->tx_dma_channel);
  }
  
  static bool spi_sam0_dma_advance_segment(struct device *dev)
  {
  	struct spi_sam0_data *data = dev->driver_data;
  	u32_t segment_len;
  
  	/* Pick the shorter buffer of ones that have an actual length */
  	if (data->ctx.rx_len != 0) {
  		segment_len = data->ctx.rx_len;
  		if (data->ctx.tx_len != 0) {
  			segment_len = MIN(segment_len, data->ctx.tx_len);
  		}
  	} else {
  		segment_len = data->ctx.tx_len;
  	}
  
  	if (segment_len == 0) {
  		return false;
  	}
  
  	segment_len = MIN(segment_len, 65535);
  
  	data->dma_segment_len = segment_len;
  	return true;
  }
  
  static int spi_sam0_dma_advance_buffers(struct device *dev)
  {
  	struct spi_sam0_data *data = dev->driver_data;
  	int retval;
  
  	if (data->dma_segment_len == 0) {
  		return -EINVAL;
  	}
  
  	/* Load receive first, so it can accept transmit data */
  	if (data->ctx.rx_len) {
  		retval = spi_sam0_dma_rx_load(dev, data->ctx.rx_buf,
  					      data->dma_segment_len);
  	} else {
  		retval = spi_sam0_dma_rx_load(dev, NULL, data->dma_segment_len);
  	}
  
  	if (retval != 0) {
  		return retval;
  	}
  
  	/* Now load the transmit, which starts the actual bus clocking */
  	if (data->ctx.tx_len) {
  		retval = spi_sam0_dma_tx_load(dev, data->ctx.tx_buf,
  					      data->dma_segment_len);
  	} else {
  		retval = spi_sam0_dma_tx_load(dev, NULL, data->dma_segment_len);
  	}
  
  	if (retval != 0) {
  		return retval;
  	}
  
  	return 0;
  }
  
  static void spi_sam0_dma_rx_done(void *arg, u32_t id, int error_code)
  {
  	struct device *dev = arg;
  	const struct spi_sam0_config *cfg = dev->config->config_info;
  	struct spi_sam0_data *data = dev->driver_data;
  	int retval;
  
  	ARG_UNUSED(id);
  	ARG_UNUSED(error_code);
  
  	spi_context_update_tx(&data->ctx, 1, data->dma_segment_len);
  	spi_context_update_rx(&data->ctx, 1, data->dma_segment_len);
  
  	if (!spi_sam0_dma_advance_segment(dev)) {
  		/* Done */
  		spi_context_cs_control(&data->ctx, false);
  		spi_context_complete(&data->ctx, 0);
  		return;
  	}
  
  	retval = spi_sam0_dma_advance_buffers(dev);
  	if (retval != 0) {
  		dma_stop(data->dma, cfg->tx_dma_channel);
  		dma_stop(data->dma, cfg->rx_dma_channel);
  		spi_context_cs_control(&data->ctx, false);
  		spi_context_complete(&data->ctx, retval);
  		return;
  	}
  }
  
  
  static int spi_sam0_transceive_async(struct device *dev,
  				     const struct spi_config *config,
  				     const struct spi_buf_set *tx_bufs,
  				     const struct spi_buf_set *rx_bufs,
  				     struct k_poll_signal *async)
  {
  	const struct spi_sam0_config *cfg = dev->config->config_info;
  	struct spi_sam0_data *data = dev->driver_data;
  	int retval;
  
  	if (!data->dma) {
  		return -ENOTSUP;
  	}
  
  	/*
  	 * Transmit clocks the output and we use receive to determine when
  	 * the transmit is done, so we always need both
  	 */
  	if (cfg->tx_dma_channel == 0xFF || cfg->rx_dma_channel == 0xFF) {
  		return -ENOTSUP;
  	}
  
  	spi_context_lock(&data->ctx, true, async);
  
  	retval = spi_sam0_configure(dev, config);
  	if (retval != 0) {
  		goto err_unlock;
  	}
  
  	spi_context_cs_control(&data->ctx, true);
  
  	spi_context_buffers_setup(&data->ctx, tx_bufs, rx_bufs, 1);
  
  	spi_sam0_dma_advance_segment(dev);
  	retval = spi_sam0_dma_advance_buffers(dev);
  	if (retval != 0) {
  		goto err_cs;
  	}
  
  	return 0;
  
  err_cs:
  	dma_stop(data->dma, cfg->tx_dma_channel);
  	dma_stop(data->dma, cfg->rx_dma_channel);
  
  	spi_context_cs_control(&data->ctx, false);
  
  err_unlock:
  	spi_context_release(&data->ctx, retval);
  	return retval;
  }
  #endif /* CONFIG_SPI_ASYNC */
  
  static int spi_sam0_release(struct device *dev,
  			    const struct spi_config *config)
  {
  	struct spi_sam0_data *data = dev->driver_data;
  
  	spi_context_unlock_unconditionally(&data->ctx);
  
  	return 0;
  }
  
  static int spi_sam0_init(struct device *dev)
  {
  	const struct spi_sam0_config *cfg = dev->config->config_info;
  	struct spi_sam0_data *data = dev->driver_data;
  	SercomSpi *regs = cfg->regs;
  
  #ifdef MCLK
  	/* Enable the GCLK */
  	GCLK->PCHCTRL[cfg->gclk_core_id].reg = GCLK_PCHCTRL_GEN_GCLK0 |
  					       GCLK_PCHCTRL_CHEN;
  
  	/* Enable the MCLK */
  	*cfg->mclk |= cfg->mclk_mask;
  #else
  	/* Enable the GCLK */
  	GCLK->CLKCTRL.reg = cfg->gclk_clkctrl_id | GCLK_CLKCTRL_GEN_GCLK0 |
  			    GCLK_CLKCTRL_CLKEN;
  
  	/* Enable SERCOM clock in PM */
  	PM->APBCMASK.reg |= cfg->pm_apbcmask;
  #endif
  
  	/* Disable all SPI interrupts */
  	regs->INTENCLR.reg = SERCOM_SPI_INTENCLR_MASK;
  	wait_synchronization(regs);
  
  #ifdef CONFIG_SPI_ASYNC
  
  	data->dma = device_get_binding(CONFIG_DMA_0_NAME);
  
  #endif
  
  	spi_context_unlock_unconditionally(&data->ctx);
  
  	/* The device will be configured and enabled when transceive
  	 * is called.
  	 */
  
  	return 0;
  }
  
  static const struct spi_driver_api spi_sam0_driver_api = {
  	.transceive = spi_sam0_transceive_sync,
  #ifdef CONFIG_SPI_ASYNC
  	.transceive_async = spi_sam0_transceive_async,
  #endif
  	.release = spi_sam0_release,
  };
  
  #if CONFIG_SPI_ASYNC
  #define SPI_SAM0_DMA_CHANNELS(n)					\
  	.tx_dma_request = ATMEL_SAM0_DT_INST_DMA_TRIGSRC(n, tx),	\
  	.tx_dma_channel = ATMEL_SAM0_DT_INST_DMA_CHANNEL(n, tx),	\
  	.rx_dma_request = ATMEL_SAM0_DT_INST_DMA_TRIGSRC(n, rx),	\
  	.rx_dma_channel = ATMEL_SAM0_DT_INST_DMA_CHANNEL(n, rx),
  #else
  #define SPI_SAM0_DMA_CHANNELS(n)
  #endif
  
  #define SPI_SAM0_SERCOM_PADS(n) \
  	SERCOM_SPI_CTRLA_DIPO(DT_INST_PROP(n, dipo)) | \
  	SERCOM_SPI_CTRLA_DOPO(DT_INST_PROP(n, dopo))
  
  #ifdef MCLK
  #define SPI_SAM0_DEFINE_CONFIG(n)					\
  static const struct spi_sam0_config spi_sam0_config_##n = {		\
  	.regs = (SercomSpi *)DT_INST_REG_ADDR(n),			\
  	.mclk = (volatile uint32_t *)MCLK_MASK_DT_INT_REG_ADDR(n),	\
  	.mclk_mask = BIT(DT_INST_CLOCKS_CELL_BY_NAME(n, mclk, bit)),	\
  	.gclk_core_id = DT_INST_CLOCKS_CELL_BY_NAME(n, gclk, periph_ch),\
  	.pads = SPI_SAM0_SERCOM_PADS(n)					\
  }
  #else
  #define SPI_SAM0_DEFINE_CONFIG(n)					\
  static const struct spi_sam0_config spi_sam0_config_##n = {		\
  	.regs = (SercomSpi *)DT_INST_REG_ADDR(n),			\
  	.pm_apbcmask = BIT(DT_INST_CLOCKS_CELL_BY_NAME(n, pm, bit)),	\
  	.gclk_clkctrl_id = DT_INST_CLOCKS_CELL_BY_NAME(n, gclk, clkctrl_id),\
  	.pads = SPI_SAM0_SERCOM_PADS(n),				\
  	SPI_SAM0_DMA_CHANNELS(n)					\
  }
  #endif /* MCLK */
  
  #define SPI_SAM0_DEVICE_INIT(n)						\
  	SPI_SAM0_DEFINE_CONFIG(n);					\
  	static struct spi_sam0_data spi_sam0_dev_data_##n = {		\
  		SPI_CONTEXT_INIT_LOCK(spi_sam0_dev_data_##n, ctx),	\
  		SPI_CONTEXT_INIT_SYNC(spi_sam0_dev_data_##n, ctx),	\
  	};								\
  	DEVICE_AND_API_INIT(spi_sam0_##n,				\
  			    DT_INST_LABEL(n),				\
  			    &spi_sam0_init, &spi_sam0_dev_data_##n,	\
  			    &spi_sam0_config_##n, POST_KERNEL,		\
  			    CONFIG_SPI_INIT_PRIORITY,			\
  			    &spi_sam0_driver_api)
  
  DT_INST_FOREACH(SPI_SAM0_DEVICE_INIT)
  