/* * Copyright (c) 2019 Derek Hageman * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include #include #include 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, \ DT_ATMEL_SAM0_I2C_SERCOM_##n##_IRQ_PRIORITY, \ i2c_sam0_isr, DEVICE_GET(i2c_sam0_##n), \ 0); \ irq_enable(DT_ATMEL_SAM0_I2C_SERCOM_##n##_IRQ); \ } #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