blob: b1cb61df6cc86c9f3638e761b1d1b17eb61e26e9 [file] [log] [blame]
/*
* Copyright (c) 2019 Derek Hageman <hageman@inthat.cloud>
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <errno.h>
#include <device.h>
#include <init.h>
#include <soc.h>
#include <drivers/i2c.h>
#include <drivers/dma.h>
#include <logging/log.h>
LOG_MODULE_REGISTER(i2c_sam0, CONFIG_I2C_LOG_LEVEL);
#include "i2c-priv.h"
struct i2c_sam0_dev_config {
SercomI2cm *regs;
u32_t bitrate;
u32_t pm_apbcmask;
u16_t gclk_clkctrl_id;
void (*irq_config_func)(struct device *dev);
#ifdef CONFIG_I2C_SAM0_DMA_DRIVEN
u8_t write_dma_request;
u8_t read_dma_request;
u8_t dma_channel;
#endif
};
struct i2c_sam0_msg {
u8_t *buffer;
u32_t size;
u32_t status;
};
struct i2c_sam0_dev_data {
struct k_sem sem;
struct i2c_sam0_msg msg;
#ifdef CONFIG_I2C_SAM0_DMA_DRIVEN
struct device *dma;
#endif
};
#define DEV_NAME(dev) ((dev)->config->name)
#define DEV_CFG(dev) \
((const struct i2c_sam0_dev_config *const)(dev)->config->config_info)
#define DEV_DATA(dev) \
((struct i2c_sam0_dev_data *const)(dev)->driver_data)
static void wait_synchronization(SercomI2cm *regs)
{
#if defined(SERCOM_I2CM_SYNCBUSY_MASK)
/* SYNCBUSY is a register */
while ((regs->SYNCBUSY.reg & SERCOM_I2CM_SYNCBUSY_MASK) != 0) {
}
#elif defined(SERCOM_I2CM_STATUS_SYNCBUSY)
/* SYNCBUSY is a bit */
while ((regs->STATUS.reg & SERCOM_I2CM_STATUS_SYNCBUSY) != 0) {
}
#else
#error Unsupported device
#endif
}
static bool i2c_sam0_terminate_on_error(struct device *dev)
{
struct i2c_sam0_dev_data *data = DEV_DATA(dev);
const struct i2c_sam0_dev_config *const cfg = DEV_CFG(dev);
SercomI2cm *i2c = cfg->regs;
if (!(i2c->STATUS.reg & (SERCOM_I2CM_STATUS_ARBLOST |
SERCOM_I2CM_STATUS_RXNACK |
#ifdef SERCOM_I2CM_STATUS_LENERR
SERCOM_I2CM_STATUS_LENERR |
#endif
#ifdef SERCOM_I2CM_STATUS_SEXTTOUT
SERCOM_I2CM_STATUS_SEXTTOUT |
#endif
#ifdef SERCOM_I2CM_STATUS_MEXTTOUT
SERCOM_I2CM_STATUS_MEXTTOUT |
#endif
SERCOM_I2CM_STATUS_LOWTOUT |
SERCOM_I2CM_STATUS_BUSERR))) {
return false;
}
#ifdef CONFIG_I2C_SAM0_DMA_DRIVEN
if (data->dma && cfg->dma_channel != 0xFF) {
dma_stop(data->dma, cfg->dma_channel);
}
#endif
data->msg.status = i2c->STATUS.reg;
/*
* Clear all the flags that require an explicit clear
* (as opposed to being cleared by ADDR writes, etc)
*/
i2c->STATUS.reg = SERCOM_I2CM_STATUS_ARBLOST |
#ifdef SERCOM_I2CM_STATUS_LENERR
SERCOM_I2CM_STATUS_LENERR |
#endif
SERCOM_I2CM_STATUS_LOWTOUT |
SERCOM_I2CM_STATUS_BUSERR;
wait_synchronization(i2c);
i2c->INTENCLR.reg = SERCOM_I2CM_INTENCLR_MASK;
k_sem_give(&data->sem);
return true;
}
static void i2c_sam0_isr(void *arg)
{
struct device *dev = (struct device *)arg;
struct i2c_sam0_dev_data *data = DEV_DATA(dev);
const struct i2c_sam0_dev_config *const cfg = DEV_CFG(dev);
SercomI2cm *i2c = cfg->regs;
/* Get present interrupts and clear them */
u32_t status = i2c->INTFLAG.reg;
i2c->INTFLAG.reg = status;
if (i2c_sam0_terminate_on_error(dev)) {
return;
}
if (status & SERCOM_I2CM_INTFLAG_MB) {
if (!data->msg.size) {
i2c->INTENCLR.reg = SERCOM_I2CM_INTENCLR_MASK;
k_sem_give(&data->sem);
return;
}
i2c->DATA.reg = *data->msg.buffer;
data->msg.buffer++;
data->msg.size--;
return;
}
if (status & SERCOM_I2CM_INTFLAG_SB) {
if (data->msg.size == 1) {
/*
* If this is the last byte, then prepare for an auto
* NACK before doing the actual read. This does not
* require write synchronization.
*/
i2c->CTRLB.bit.ACKACT = 1;
}
*data->msg.buffer = i2c->DATA.reg;
data->msg.buffer++;
data->msg.size--;
if (!data->msg.size) {
i2c->INTENCLR.reg = SERCOM_I2CM_INTENCLR_MASK;
k_sem_give(&data->sem);
return;
}
return;
}
}
#ifdef CONFIG_I2C_SAM0_DMA_DRIVEN
static void i2c_sam0_dma_write_done(void *arg, u32_t id, int error_code)
{
struct device *dev = arg;
struct i2c_sam0_dev_data *data = DEV_DATA(dev);
const struct i2c_sam0_dev_config *const cfg = DEV_CFG(dev);
SercomI2cm *i2c = cfg->regs;
ARG_UNUSED(id);
int key = irq_lock();
if (i2c_sam0_terminate_on_error(dev)) {
irq_unlock(key);
return;
}
if (error_code < 0) {
LOG_ERR("DMA write error on %s: %d", DEV_NAME(dev), error_code);
i2c->INTENCLR.reg = SERCOM_I2CM_INTENCLR_MASK;
irq_unlock(key);
data->msg.status = error_code;
k_sem_give(&data->sem);
return;
}
irq_unlock(key);
/*
* DMA has written the whole message now, so just wait for the
* final I2C IRQ to indicate that it's finished transmitting.
*/
data->msg.size = 0;
i2c->INTENSET.reg = SERCOM_I2CM_INTENSET_MB;
}
static bool i2c_sam0_dma_write_start(struct device *dev)
{
struct i2c_sam0_dev_data *data = DEV_DATA(dev);
const struct i2c_sam0_dev_config *const cfg = DEV_CFG(dev);
SercomI2cm *i2c = cfg->regs;
int retval;
if (!data->dma) {
return false;
}
if (cfg->dma_channel == 0xFF) {
return false;
}
if (data->msg.size <= 1) {
/*
* Catch empty writes and skip DMA on single byte transfers.
*/
return false;
}
struct dma_config dma_cfg = { 0 };
struct dma_block_config dma_blk = { 0 };
dma_cfg.channel_direction = MEMORY_TO_PERIPHERAL;
dma_cfg.source_data_size = 1;
dma_cfg.dest_data_size = 1;
dma_cfg.callback_arg = dev;
dma_cfg.dma_callback = i2c_sam0_dma_write_done;
dma_cfg.block_count = 1;
dma_cfg.head_block = &dma_blk;
dma_cfg.dma_slot = cfg->write_dma_request;
dma_blk.block_size = data->msg.size;
dma_blk.source_address = (u32_t)data->msg.buffer;
dma_blk.dest_address = (u32_t)(&(i2c->DATA.reg));
dma_blk.dest_addr_adj = DMA_ADDR_ADJ_NO_CHANGE;
retval = dma_config(data->dma, cfg->dma_channel, &dma_cfg);
if (retval != 0) {
LOG_ERR("Write DMA configure on %s failed: %d",
DEV_NAME(dev), retval);
return false;
}
retval = dma_start(data->dma, cfg->dma_channel);
if (retval != 0) {
LOG_ERR("Write DMA start on %s failed: %d",
DEV_NAME(dev), retval);
return false;
}
return true;
}
static void i2c_sam0_dma_read_done(void *arg, u32_t id, int error_code)
{
struct device *dev = arg;
struct i2c_sam0_dev_data *data = DEV_DATA(dev);
const struct i2c_sam0_dev_config *const cfg = DEV_CFG(dev);
SercomI2cm *i2c = cfg->regs;
ARG_UNUSED(id);
int key = irq_lock();
if (i2c_sam0_terminate_on_error(dev)) {
irq_unlock(key);
return;
}
if (error_code < 0) {
LOG_ERR("DMA read error on %s: %d", DEV_NAME(dev), error_code);
i2c->INTENCLR.reg = SERCOM_I2CM_INTENCLR_MASK;
irq_unlock(key);
data->msg.status = error_code;
k_sem_give(&data->sem);
return;
}
irq_unlock(key);
/*
* DMA has read all but the last byte now, so let the ISR handle
* that and the terminating NACK.
*/
data->msg.buffer += data->msg.size - 1;
data->msg.size = 1;
i2c->INTENSET.reg = SERCOM_I2CM_INTENSET_SB;
}
static bool i2c_sam0_dma_read_start(struct device *dev)
{
struct i2c_sam0_dev_data *data = DEV_DATA(dev);
const struct i2c_sam0_dev_config *const cfg = DEV_CFG(dev);
SercomI2cm *i2c = cfg->regs;
int retval;
if (!data->dma) {
return false;
}
if (cfg->dma_channel == 0xFF) {
return false;
}
if (data->msg.size <= 2) {
/*
* The last byte is always handled by the I2C ISR so
* just skip a two length read as well.
*/
return false;
}
struct dma_config dma_cfg = { 0 };
struct dma_block_config dma_blk = { 0 };
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 = i2c_sam0_dma_read_done;
dma_cfg.block_count = 1;
dma_cfg.head_block = &dma_blk;
dma_cfg.dma_slot = cfg->read_dma_request;
dma_blk.block_size = data->msg.size - 1;
dma_blk.dest_address = (u32_t)data->msg.buffer;
dma_blk.source_address = (u32_t)(&(i2c->DATA.reg));
dma_blk.source_addr_adj = DMA_ADDR_ADJ_NO_CHANGE;
retval = dma_config(data->dma, cfg->dma_channel, &dma_cfg);
if (retval != 0) {
LOG_ERR("Read DMA configure on %s failed: %d",
DEV_NAME(dev), retval);
return false;
}
retval = dma_start(data->dma, cfg->dma_channel);
if (retval != 0) {
LOG_ERR("Read DMA start on %s failed: %d",
DEV_NAME(dev), retval);
return false;
}
return true;
}
#endif
static int i2c_sam0_transfer(struct device *dev, struct i2c_msg *msgs,
u8_t num_msgs, u16_t addr)
{
struct i2c_sam0_dev_data *data = DEV_DATA(dev);
const struct i2c_sam0_dev_config *const cfg = DEV_CFG(dev);
SercomI2cm *i2c = cfg->regs;
u32_t addr_reg;
if (!num_msgs) {
return 0;
}
for (; num_msgs > 0;) {
if (!msgs->len) {
if ((msgs->flags & I2C_MSG_RW_MASK) == I2C_MSG_READ) {
return -EINVAL;
}
}
i2c->INTENCLR.reg = SERCOM_I2CM_INTENCLR_MASK;
i2c->INTFLAG.reg = SERCOM_I2CM_INTFLAG_MASK;
i2c->STATUS.reg = SERCOM_I2CM_STATUS_ARBLOST |
#ifdef SERCOM_I2CM_STATUS_LENERR
SERCOM_I2CM_STATUS_LENERR |
#endif
SERCOM_I2CM_STATUS_LOWTOUT |
SERCOM_I2CM_STATUS_BUSERR;
wait_synchronization(i2c);
data->msg.buffer = msgs->buf;
data->msg.size = msgs->len;
data->msg.status = 0;
addr_reg = addr << 1U;
if ((msgs->flags & I2C_MSG_RW_MASK) == I2C_MSG_READ) {
addr_reg |= 1U;
/* Set to auto ACK */
i2c->CTRLB.bit.ACKACT = 0;
wait_synchronization(i2c);
}
if (msgs->flags & I2C_MSG_ADDR_10_BITS) {
#ifdef SERCOM_I2CM_ADDR_TENBITEN
addr_reg |= SERCOM_I2CM_ADDR_TENBITEN;
#else
return -ENOTSUP;
#endif
}
int key = irq_lock();
/*
* Writing the address starts the transaction, issuing
* a start/repeated start as required.
*/
i2c->ADDR.reg = addr_reg;
/*
* Have to wait here to make sure the address write
* clears any pending requests or errors before DMA or
* ISR tries to handle it.
*/
wait_synchronization(i2c);
#ifdef SERCOM_I2CM_INTENSET_ERROR
i2c->INTENSET.reg = SERCOM_I2CM_INTENSET_ERROR;
#endif
if ((msgs->flags & I2C_MSG_RW_MASK) == I2C_MSG_READ) {
/*
* Always set MB even when reading, since that's how
* some errors are indicated.
*/
i2c->INTENSET.reg = SERCOM_I2CM_INTENSET_MB;
#ifdef CONFIG_I2C_SAM0_DMA_DRIVEN
if (!i2c_sam0_dma_read_start(dev))
#endif
{
i2c->INTENSET.reg = SERCOM_I2CM_INTENSET_SB;
}
} else {
#ifdef CONFIG_I2C_SAM0_DMA_DRIVEN
if (!i2c_sam0_dma_write_start(dev))
#endif
{
i2c->INTENSET.reg = SERCOM_I2CM_INTENSET_MB;
}
}
irq_unlock(key);
/* Now wait for the ISR to handle everything */
k_sem_take(&data->sem, K_FOREVER);
if (data->msg.status) {
if (data->msg.status & SERCOM_I2CM_STATUS_ARBLOST) {
LOG_DBG("Arbitration lost on %s",
DEV_NAME(dev));
return -EAGAIN;
}
LOG_ERR("Transaction error on %s: %08X",
DEV_NAME(dev), data->msg.status);
return -EIO;
}
if (msgs->flags & I2C_MSG_STOP) {
i2c->CTRLB.bit.CMD = 3;
} else if ((msgs->flags & I2C_MSG_RESTART) && num_msgs > 1) {
/*
* No action, since we do this automatically if we
* don't send an explicit stop
*/
} else {
/*
* Neither present, so assume we want to release
* the bus (by sending a stop)
*/
i2c->CTRLB.bit.CMD = 3;
}
num_msgs--;
msgs++;
}
return 0;
}
static int i2c_sam0_set_apply_bitrate(struct device *dev, u32_t config)
{
const struct i2c_sam0_dev_config *const cfg = DEV_CFG(dev);
SercomI2cm *i2c = cfg->regs;
u32_t baud;
u32_t baud_low;
u32_t baud_high;
u32_t CTRLA = i2c->CTRLA.reg;
#ifdef SERCOM_I2CM_CTRLA_SPEED_Msk
CTRLA &= ~SERCOM_I2CM_CTRLA_SPEED_Msk;
#endif
CTRLA &= ~SERCOM_I2CM_CTRLA_SDAHOLD_Msk;
switch (I2C_SPEED_GET(config)) {
case I2C_SPEED_STANDARD:
#ifdef SERCOM_I2CM_CTRLA_SPEED
CTRLA |= SERCOM_I2CM_CTRLA_SPEED(0);
#endif
CTRLA |= SERCOM_I2CM_CTRLA_SDAHOLD(0x0);
i2c->CTRLA.reg = CTRLA;
wait_synchronization(i2c);
/* 5 is the nominal 100ns rise time from the app notes */
baud = (SOC_ATMEL_SAM0_GCLK0_FREQ_HZ / 100000U - 5U - 10U) / 2U;
if (baud > 255U || baud < 1U) {
return -ERANGE;
}
LOG_DBG("Setting %s to standard mode with divisor %u",
DEV_NAME(dev), baud);
i2c->BAUD.reg = SERCOM_I2CM_BAUD_BAUD(baud);
break;
case I2C_SPEED_FAST:
CTRLA |= SERCOM_I2CM_CTRLA_SDAHOLD(0x0);
i2c->CTRLA.reg = CTRLA;
wait_synchronization(i2c);
/* 5 is the nominal 100ns rise time from the app notes */
baud = (SOC_ATMEL_SAM0_GCLK0_FREQ_HZ / 400000U - 5U - 10U) / 2U;
if (baud > 255U || baud < 1U) {
return -ERANGE;
}
LOG_DBG("Setting %s to fast mode with divisor %u",
DEV_NAME(dev), baud);
i2c->BAUD.reg = SERCOM_I2CM_BAUD_BAUD(baud);
break;
case I2C_SPEED_FAST_PLUS:
#ifdef SERCOM_I2CM_CTRLA_SPEED
CTRLA |= SERCOM_I2CM_CTRLA_SPEED(1);
#endif
CTRLA |= SERCOM_I2CM_CTRLA_SDAHOLD(0x2);
i2c->CTRLA.reg = CTRLA;
wait_synchronization(i2c);
/* 5 is the nominal 100ns rise time from the app notes */
baud = (SOC_ATMEL_SAM0_GCLK0_FREQ_HZ / 1000000U - 5U - 10U);
/* 2:1 low:high ratio */
baud_high = baud;
baud_high /= 3U;
baud_high = MAX(MIN(baud_high, 255U), 1U);
baud_low = baud - baud_high;
if (baud_low < 1U && baud_high > 1U) {
--baud_high;
++baud_low;
}
if (baud_low < 1U || baud_low > 255U) {
return -ERANGE;
}
LOG_DBG("Setting %s to fast mode plus with divisors %u/%u",
DEV_NAME(dev), baud_high, baud_low);
i2c->BAUD.reg = SERCOM_I2CM_BAUD_BAUD(baud_high) |
SERCOM_I2CM_BAUD_BAUDLOW(baud_low);
break;
case I2C_SPEED_HIGH:
#ifdef SERCOM_I2CM_CTRLA_SPEED
CTRLA |= SERCOM_I2CM_CTRLA_SPEED(2);
#endif
CTRLA |= SERCOM_I2CM_CTRLA_SDAHOLD(0x2);
i2c->CTRLA.reg = CTRLA;
wait_synchronization(i2c);
baud = (SOC_ATMEL_SAM0_GCLK0_FREQ_HZ / 3400000U) - 2U;
/* 2:1 low:high ratio */
baud_high = baud;
baud_high /= 3U;
baud_high = MAX(MIN(baud_high, 255U), 1U);
baud_low = baud - baud_high;
if (baud_low < 1U && baud_high > 1U) {
--baud_high;
++baud_low;
}
if (baud_low < 1U || baud_low > 255U) {
return -ERANGE;
}
#ifdef SERCOM_I2CM_BAUD_HSBAUD
LOG_DBG("Setting %s to high speed with divisors %u/%u",
DEV_NAME(dev), baud_high, baud_low);
/*
* 48 is just from the app notes, but the datasheet says
* it's ignored
*/
i2c->BAUD.reg = SERCOM_I2CM_BAUD_HSBAUD(baud_high) |
SERCOM_I2CM_BAUD_HSBAUDLOW(baud_low) |
SERCOM_I2CM_BAUD_BAUD(48) |
SERCOM_I2CM_BAUD_BAUDLOW(48);
#else
return -ENOTSUP;
#endif
break;
default:
return -ENOTSUP;
}
wait_synchronization(i2c);
return 0;
}
static int i2c_sam0_configure(struct device *dev, u32_t config)
{
const struct i2c_sam0_dev_config *const cfg = DEV_CFG(dev);
SercomI2cm *i2c = cfg->regs;
int retval;
if (!(config & I2C_MODE_MASTER)) {
return -EINVAL;
}
if (config & I2C_SPEED_MASK) {
i2c->CTRLA.bit.ENABLE = 0;
wait_synchronization(i2c);
retval = i2c_sam0_set_apply_bitrate(dev, config);
i2c->CTRLA.bit.ENABLE = 1;
wait_synchronization(i2c);
if (retval != 0) {
return retval;
}
}
return 0;
}
static int i2c_sam0_initialize(struct device *dev)
{
struct i2c_sam0_dev_data *data = DEV_DATA(dev);
const struct i2c_sam0_dev_config *const cfg = DEV_CFG(dev);
SercomI2cm *i2c = cfg->regs;
int retval;
/* 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;
/* Disable all I2C interrupts */
i2c->INTENCLR.reg = SERCOM_I2CM_INTENCLR_MASK;
/* I2C mode, enable timeouts */
i2c->CTRLA.reg = SERCOM_I2CM_CTRLA_MODE_I2C_MASTER |
#ifdef SERCOM_I2CM_CTRLA_LOWTOUTEN
SERCOM_I2CM_CTRLA_LOWTOUTEN |
#endif
SERCOM_I2CM_CTRLA_INACTOUT(0x3);
wait_synchronization(i2c);
/* Enable smart mode (auto ACK) */
i2c->CTRLB.reg = SERCOM_I2CM_CTRLB_SMEN;
wait_synchronization(i2c);
retval = i2c_sam0_set_apply_bitrate(dev,
i2c_map_dt_bitrate(cfg->bitrate));
if (retval != 0) {
return retval;
}
k_sem_init(&data->sem, 0, 1);
cfg->irq_config_func(dev);
#ifdef CONFIG_I2C_SAM0_DMA_DRIVEN
data->dma = device_get_binding(CONFIG_DMA_0_NAME);
#endif
i2c->CTRLA.bit.ENABLE = 1;
wait_synchronization(i2c);
/* Force bus idle */
i2c->STATUS.bit.BUSSTATE = 1;
wait_synchronization(i2c);
return 0;
}
static const struct i2c_driver_api i2c_sam0_driver_api = {
.configure = i2c_sam0_configure,
.transfer = i2c_sam0_transfer,
};
#ifdef CONFIG_I2C_SAM0_DMA_DRIVEN
#ifndef DT_ATMEL_SAM0_I2C_SERCOM_0_DMA
#define DT_ATMEL_SAM0_I2C_SERCOM_0_DMA 0xFF
#endif
#ifndef DT_ATMEL_SAM0_I2C_SERCOM_1_DMA
#define DT_ATMEL_SAM0_I2C_SERCOM_1_DMA 0xFF
#endif
#ifndef DT_ATMEL_SAM0_I2C_SERCOM_2_DMA
#define DT_ATMEL_SAM0_I2C_SERCOM_2_DMA 0xFF
#endif
#ifndef DT_ATMEL_SAM0_I2C_SERCOM_3_DMA
#define DT_ATMEL_SAM0_I2C_SERCOM_3_DMA 0xFF
#endif
#ifndef DT_ATMEL_SAM0_I2C_SERCOM_4_DMA
#define DT_ATMEL_SAM0_I2C_SERCOM_4_DMA 0xFF
#endif
#ifndef DT_ATMEL_SAM0_I2C_SERCOM_5_DMA
#define DT_ATMEL_SAM0_I2C_SERCOM_5_DMA 0xFF
#endif
#ifndef DT_ATMEL_SAM0_I2C_SERCOM_6_DMA
#define DT_ATMEL_SAM0_I2C_SERCOM_6_DMA 0xFF
#endif
#ifndef DT_ATMEL_SAM0_I2C_SERCOM_7_DMA
#define DT_ATMEL_SAM0_I2C_SERCOM_7_DMA 0xFF
#endif
#define I2C_SAM0_DMA_CHANNELS(n) \
.write_dma_request = SERCOM##n##_DMAC_ID_TX, \
.read_dma_request = SERCOM##n##_DMAC_ID_RX, \
.dma_channel = DT_ATMEL_SAM0_I2C_SERCOM_##n##_DMA,
#else
#define I2C_SAM0_DMA_CHANNELS(n)
#endif
#define I2C_SAM0_DEVICE(n) \
static void i2c_sam_irq_config_##n(struct device *dev); \
static const struct i2c_sam0_dev_config i2c_sam0_dev_config_##n = { \
.regs = (SercomI2cm *)DT_ATMEL_SAM0_I2C_SERCOM_##n##_BASE_ADDRESS, \
.bitrate = DT_ATMEL_SAM0_I2C_SERCOM_##n##_CLOCK_FREQUENCY, \
.pm_apbcmask = PM_APBCMASK_SERCOM##n, \
.gclk_clkctrl_id = GCLK_CLKCTRL_ID_SERCOM##n##_CORE, \
.irq_config_func = &i2c_sam_irq_config_##n, \
I2C_SAM0_DMA_CHANNELS(n) \
}; \
static struct i2c_sam0_dev_data i2c_sam0_dev_data_##n; \
DEVICE_AND_API_INIT(i2c_sam0_##n, \
DT_ATMEL_SAM0_I2C_SERCOM_##n##_LABEL, \
&i2c_sam0_initialize, &i2c_sam0_dev_data_##n, \
&i2c_sam0_dev_config_##n, POST_KERNEL, \
CONFIG_I2C_INIT_PRIORITY, &i2c_sam0_driver_api);\
static void i2c_sam_irq_config_##n(struct device *dev) \
{ \
IRQ_CONNECT(DT_ATMEL_SAM0_I2C_SERCOM_##n##_IRQ_0, \
DT_ATMEL_SAM0_I2C_SERCOM_##n##_IRQ_0_PRIORITY, \
i2c_sam0_isr, DEVICE_GET(i2c_sam0_##n), \
0); \
irq_enable(DT_ATMEL_SAM0_I2C_SERCOM_##n##_IRQ_0); \
}
#if DT_ATMEL_SAM0_I2C_SERCOM_0_BASE_ADDRESS
I2C_SAM0_DEVICE(0);
#endif
#if DT_ATMEL_SAM0_I2C_SERCOM_1_BASE_ADDRESS
I2C_SAM0_DEVICE(1);
#endif
#if DT_ATMEL_SAM0_I2C_SERCOM_2_BASE_ADDRESS
I2C_SAM0_DEVICE(2);
#endif
#if DT_ATMEL_SAM0_I2C_SERCOM_3_BASE_ADDRESS
I2C_SAM0_DEVICE(3);
#endif
#if DT_ATMEL_SAM0_I2C_SERCOM_4_BASE_ADDRESS
I2C_SAM0_DEVICE(4);
#endif
#if DT_ATMEL_SAM0_I2C_SERCOM_5_BASE_ADDRESS
I2C_SAM0_DEVICE(5);
#endif
#if DT_ATMEL_SAM0_I2C_SERCOM_6_BASE_ADDRESS
I2C_SAM0_DEVICE(6);
#endif
#if DT_ATMEL_SAM0_I2C_SERCOM_7_BASE_ADDRESS
I2C_SAM0_DEVICE(7);
#endif