657 lines
16 KiB
C
657 lines
16 KiB
C
/*
|
|
* Copyright (c) 2016 Intel Corporation
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
/**
|
|
* @file I2C/TWI Controller driver for Atmel SAM3 family processor
|
|
*
|
|
* @deprecated This driver is deprecated. Please use i2c_sam_twi.c SAM family
|
|
* driver instead.
|
|
*
|
|
* Notes on this driver:
|
|
* 1. The controller does not have a documented way to
|
|
* issue RESTART when changing transfer direction as master.
|
|
*
|
|
* Datasheet said about using the internal address register
|
|
* (IADR) to write 3 bytes before reading. This limits
|
|
* the number of bytes to write before a read. Also,
|
|
* this was documented under 7-bit addressing, and nothing
|
|
* about this with 10-bit addressing.
|
|
*
|
|
* Experiments show that STOP has to be issued or the controller
|
|
* hangs forever. This was tested with reading and writing
|
|
* the Fujitsu I2C-based FRAM MB85RC256V.
|
|
*/
|
|
|
|
#include <errno.h>
|
|
|
|
#include <kernel.h>
|
|
|
|
#include <board.h>
|
|
#include <i2c.h>
|
|
#include <sys_clock.h>
|
|
|
|
#include <misc/util.h>
|
|
|
|
#define SYS_LOG_LEVEL CONFIG_SYS_LOG_I2C_LEVEL
|
|
#include <logging/sys_log.h>
|
|
|
|
#define TWI_IRQ_PDC \
|
|
(TWI_SR_ENDRX | TWI_SR_ENDTX | TWI_SR_RXBUFF | TWI_SR_TXBUFE)
|
|
|
|
/* for use with dev_data->state */
|
|
#define STATE_READY 0
|
|
#define STATE_BUSY (1 << 0)
|
|
#define STATE_TX (1 << 1)
|
|
#define STATE_RX (1 << 2)
|
|
|
|
/* return values for internal functions */
|
|
#define RET_OK 0
|
|
#define RET_ERR 1
|
|
#define RET_NACK 2
|
|
|
|
|
|
typedef void (*config_func_t)(struct device *dev);
|
|
|
|
|
|
struct i2c_sam3_dev_config {
|
|
Twi *regs;
|
|
config_func_t config_func;
|
|
};
|
|
|
|
struct i2c_sam3_dev_data {
|
|
struct k_sem device_sync_sem;
|
|
u32_t dev_config;
|
|
|
|
volatile u32_t state;
|
|
|
|
u8_t *xfr_buf;
|
|
u32_t xfr_len;
|
|
u32_t xfr_flags;
|
|
};
|
|
|
|
|
|
/**
|
|
* Calculate clock dividers for TWI controllers.
|
|
*
|
|
* @param dev Device struct
|
|
* @return Value used for TWI_CWGR register.
|
|
*/
|
|
static u32_t clk_div_calc(struct device *dev)
|
|
{
|
|
#if (CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC == 84000000)
|
|
|
|
/* Use pre-calculated clock dividers when the SoC is running at
|
|
* 84 MHz. This saves execution time and ROM space.
|
|
*/
|
|
struct i2c_sam3_dev_data * const dev_data = dev->driver_data;
|
|
|
|
switch (I2C_SPEED_GET(dev_data->dev_config)) {
|
|
case I2C_SPEED_STANDARD:
|
|
/* CKDIV = 1
|
|
* CHDIV = CLDIV = 208 = 0xD0
|
|
*/
|
|
return 0x0001D0D0;
|
|
case I2C_SPEED_FAST:
|
|
/* CKDIV = 0
|
|
* CHDIV = 101 = 0x65
|
|
* CLDIV = 106 = 0x6A
|
|
*/
|
|
return 0x0000656A;
|
|
default:
|
|
/* Return 0 as error */
|
|
return 0;
|
|
}
|
|
|
|
#else /* !(CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC == 84000000) */
|
|
|
|
/* Need to calcualte the clock dividers if the SoC is running at
|
|
* other frequencies.
|
|
*/
|
|
|
|
struct i2c_sam3_dev_data * const dev_data = dev->driver_data;
|
|
u32_t i2c_clk;
|
|
u32_t cldiv, chdiv, ckdiv;
|
|
u32_t i2c_h_min_time, i2c_l_min_time;
|
|
u32_t cldiv_min, chdiv_min;
|
|
u32_t mck;
|
|
|
|
/* The T(low) and T(high) are used to calculate CLDIV and CHDIV.
|
|
* Since we treat both clock low and clock high to have same period,
|
|
* the I2C clock frequency used for calculation has to be doubled.
|
|
*
|
|
* The I2C spec has the following minimum timing requirement:
|
|
* Standard Speed: High 4000ns, Low 4700ns
|
|
* Fast Speed: High 600ns, Low 1300ns
|
|
*
|
|
* So use these to calculate chdiv_min and cldiv_min.
|
|
*/
|
|
switch (I2C_SPEED_GET(dev_data->dev_config)) {
|
|
case I2C_SPEED_STANDARD:
|
|
i2c_clk = 100000 * 2;
|
|
i2c_h_min_time = 4000;
|
|
i2c_l_min_time = 4700;
|
|
break;
|
|
case I2C_SPEED_FAST:
|
|
i2c_clk = 400000 * 2;
|
|
i2c_h_min_time = 600;
|
|
i2c_l_min_time = 1300;
|
|
break;
|
|
default:
|
|
/* Return 0 as error */
|
|
return 0;
|
|
}
|
|
|
|
/* Calculate CLDIV (which will be used for CHDIV also) */
|
|
cldiv = (CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC) / i2c_clk - 4;
|
|
|
|
/* Calculate minimum CHDIV and CLDIV */
|
|
|
|
/* Make 1/mck be in micro second */
|
|
mck = CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC
|
|
/ MSEC_PER_SEC / USEC_PER_MSEC;
|
|
|
|
/* The +1 is to make sure we don't go under the minimum
|
|
* after the division. In other words, force rounding up.
|
|
*/
|
|
cldiv_min = (i2c_l_min_time * mck / 1000) - 4 + 1;
|
|
chdiv_min = (i2c_h_min_time * mck / 1000) - 4 + 1;
|
|
|
|
ckdiv = 0;
|
|
while (cldiv > 255) {
|
|
ckdiv++;
|
|
|
|
/* Math is there to round up.
|
|
* Rounding up makes the SCL periods longer,
|
|
* which makes clock slower.
|
|
* This is fine as faster clock may cause
|
|
* issues.
|
|
*/
|
|
cldiv = (cldiv >> 1) + (cldiv & 0x01);
|
|
|
|
cldiv_min = (cldiv_min >> 1) + (cldiv_min & 0x01);
|
|
chdiv_min = (chdiv_min >> 1) + (chdiv_min & 0x01);
|
|
}
|
|
|
|
chdiv = cldiv;
|
|
|
|
/* Make sure we are above minimum requirements */
|
|
cldiv = max(cldiv, cldiv_min);
|
|
chdiv = max(chdiv, chdiv_min);
|
|
|
|
return TWI_CWGR_CKDIV(ckdiv) + TWI_CWGR_CHDIV(chdiv)
|
|
+ TWI_CWGR_CLDIV(cldiv);
|
|
|
|
#endif /* CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC == 84000000 */
|
|
}
|
|
|
|
static int i2c_sam3_runtime_configure(struct device *dev, u32_t config)
|
|
{
|
|
const struct i2c_sam3_dev_config * const cfg = dev->config->config_info;
|
|
struct i2c_sam3_dev_data * const dev_data = dev->driver_data;
|
|
u32_t reg;
|
|
u32_t clk;
|
|
|
|
dev_data->dev_config = config;
|
|
reg = 0;
|
|
|
|
/* Calculate clock dividers */
|
|
clk = clk_div_calc(dev);
|
|
if (!clk) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* Disable controller first before changing anything */
|
|
cfg->regs->TWI_CR = TWI_CR_MSDIS | TWI_CR_SVDIS;
|
|
|
|
/* Setup clock wavefore generator */
|
|
cfg->regs->TWI_CWGR = clk;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void i2c_sam3_isr(void *arg)
|
|
{
|
|
struct device * const dev = (struct device *)arg;
|
|
const struct i2c_sam3_dev_config *const cfg = dev->config->config_info;
|
|
struct i2c_sam3_dev_data * const dev_data = dev->driver_data;
|
|
|
|
/* Disable all interrupts so they can be processed
|
|
* before ISR is called again.
|
|
*/
|
|
cfg->regs->TWI_IDR = cfg->regs->TWI_IMR;
|
|
|
|
k_sem_give(&dev_data->device_sync_sem);
|
|
}
|
|
|
|
/* This should be used ONLY IF <bits> are the only bits of concern.
|
|
* This is because reading from status register will clear certain
|
|
* bits, and thus status might be ignored afterwards.
|
|
*/
|
|
static inline void sr_bits_set_wait(struct device *dev, u32_t bits)
|
|
{
|
|
const struct i2c_sam3_dev_config *const cfg = dev->config->config_info;
|
|
|
|
while (!(cfg->regs->TWI_SR & bits)) {
|
|
/* loop till <bits> are set */
|
|
};
|
|
}
|
|
|
|
/* Clear the status registers from previous transfers */
|
|
static inline void status_reg_clear(struct device *dev)
|
|
{
|
|
const struct i2c_sam3_dev_config *const cfg = dev->config->config_info;
|
|
u32_t stat_reg;
|
|
|
|
do {
|
|
stat_reg = cfg->regs->TWI_SR;
|
|
|
|
/* ignore these */
|
|
stat_reg &= ~(TWI_IRQ_PDC | TWI_SR_TXRDY | TWI_SR_TXCOMP
|
|
| TWI_SR_SVREAD);
|
|
|
|
if (stat_reg & TWI_SR_OVRE) {
|
|
continue;
|
|
}
|
|
|
|
if (stat_reg & TWI_SR_NACK) {
|
|
continue;
|
|
}
|
|
|
|
if (stat_reg & TWI_SR_RXRDY) {
|
|
stat_reg = cfg->regs->TWI_RHR;
|
|
}
|
|
} while (stat_reg);
|
|
}
|
|
|
|
static inline void transfer_setup(struct device *dev, u16_t slave_address)
|
|
{
|
|
const struct i2c_sam3_dev_config *const cfg = dev->config->config_info;
|
|
struct i2c_sam3_dev_data * const dev_data = dev->driver_data;
|
|
u32_t mmr;
|
|
u32_t iadr;
|
|
|
|
/* Set slave address */
|
|
if (I2C_ADDR_10_BITS & dev_data->dev_config) {
|
|
/* 10-bit slave addressing:
|
|
* first two bits goes to MMR/DADR, other 8 to IADR.
|
|
*
|
|
* 0x78 is the 0b11110xx bit prefix.
|
|
*/
|
|
mmr = TWI_MMR_DADR(0x78 | ((slave_address >> 8) & 0x03));
|
|
mmr |= TWI_MMR_IADRSZ_1_BYTE;
|
|
|
|
iadr = slave_address & 0xFF;
|
|
} else {
|
|
/* 7-bit slave addressing */
|
|
mmr = TWI_MMR_DADR(slave_address);
|
|
|
|
iadr = 0;
|
|
}
|
|
|
|
cfg->regs->TWI_MMR = mmr;
|
|
cfg->regs->TWI_IADR = iadr;
|
|
}
|
|
|
|
static inline int msg_write(struct device *dev)
|
|
{
|
|
const struct i2c_sam3_dev_config *const cfg = dev->config->config_info;
|
|
struct i2c_sam3_dev_data * const dev_data = dev->driver_data;
|
|
|
|
/* To write to slave */
|
|
cfg->regs->TWI_MMR &= ~TWI_MMR_MREAD;
|
|
|
|
/* Setup PDC to do DMA transfer */
|
|
cfg->regs->TWI_PTCR = TWI_PTCR_TXTDIS | TWI_PTCR_RXTDIS;
|
|
cfg->regs->TWI_TPR = (u32_t)dev_data->xfr_buf;
|
|
cfg->regs->TWI_TCR = dev_data->xfr_len;
|
|
|
|
/* Enable TX related interrupts.
|
|
* TXRDY is used by PDC so we don't want to interfere.
|
|
*/
|
|
cfg->regs->TWI_IER = TWI_SR_ENDTX | TWI_SR_NACK;
|
|
|
|
/* Start DMA transfer for TX */
|
|
cfg->regs->TWI_PTCR = TWI_PTCR_TXTEN;
|
|
|
|
/* Wait till transfer is done or error occurs */
|
|
k_sem_take(&dev_data->device_sync_sem, K_FOREVER);
|
|
|
|
/* Check for error */
|
|
if (cfg->regs->TWI_SR & TWI_SR_NACK) {
|
|
return RET_NACK;
|
|
}
|
|
|
|
/* STOP if needed */
|
|
if (dev_data->xfr_flags & I2C_MSG_STOP) {
|
|
cfg->regs->TWI_CR = TWI_CR_STOP;
|
|
|
|
/* Wait for TXCOMP if sending STOP.
|
|
* The transfer is done and the controller just needs to
|
|
* 'send' the STOP bit. So wait should be very short.
|
|
*/
|
|
sr_bits_set_wait(dev, TWI_SR_TXCOMP);
|
|
} else {
|
|
/* If no STOP, just wait for TX buffer to clear.
|
|
* At this point, this should take no time.
|
|
*/
|
|
sr_bits_set_wait(dev, TWI_SR_TXRDY);
|
|
}
|
|
|
|
/* Disable PDC */
|
|
cfg->regs->TWI_PTCR = TWI_PTCR_TXTDIS;
|
|
|
|
return RET_OK;
|
|
}
|
|
|
|
static inline int msg_read(struct device *dev)
|
|
{
|
|
const struct i2c_sam3_dev_config *const cfg = dev->config->config_info;
|
|
struct i2c_sam3_dev_data * const dev_data = dev->driver_data;
|
|
u32_t stat_reg;
|
|
u32_t ctrl_reg;
|
|
u32_t last_len;
|
|
|
|
/* To read from slave */
|
|
cfg->regs->TWI_MMR |= TWI_MMR_MREAD;
|
|
|
|
/* START bit in control register needs to be set to start
|
|
* reading from slave. If the previous message is also read,
|
|
* there is no need to set the START bit again.
|
|
*/
|
|
ctrl_reg = 0;
|
|
if (dev_data->xfr_flags & I2C_MSG_RESTART) {
|
|
ctrl_reg = TWI_CR_START;
|
|
}
|
|
/* If there is only one byte to read, need to send STOP also. */
|
|
if ((dev_data->xfr_len == 1)
|
|
&& (dev_data->xfr_flags & I2C_MSG_STOP)) {
|
|
ctrl_reg |= TWI_CR_STOP;
|
|
dev_data->xfr_flags &= ~I2C_MSG_STOP;
|
|
}
|
|
cfg->regs->TWI_CR = ctrl_reg;
|
|
|
|
/* Note that this is entirely possible to do the last byte without
|
|
* going through DMA. But that requires another block of code to
|
|
* setup the transfer and test for RXRDY bit (and other). So do it
|
|
* this way to save a few bytes of code space.
|
|
*/
|
|
while (dev_data->xfr_len > 0) {
|
|
/* Setup PDC to do DMA transfer. */
|
|
cfg->regs->TWI_PTCR = TWI_PTCR_TXTDIS | TWI_PTCR_RXTDIS;
|
|
cfg->regs->TWI_RPR = (u32_t)dev_data->xfr_buf;
|
|
|
|
/* Note that we need to set the STOP bit before reading
|
|
* last byte from RHR. So we need to process the last byte
|
|
* differently.
|
|
*/
|
|
if (dev_data->xfr_len > 1) {
|
|
last_len = dev_data->xfr_len - 1;
|
|
} else {
|
|
last_len = 1;
|
|
|
|
/* Set STOP bit for last byte.
|
|
* The extra check here is to prevent setting
|
|
* TWI_CR_STOP twice, when the message length
|
|
* is 1, as it is already set above.
|
|
*/
|
|
if (dev_data->xfr_flags & I2C_MSG_STOP) {
|
|
cfg->regs->TWI_CR = TWI_CR_STOP;
|
|
}
|
|
}
|
|
cfg->regs->TWI_RCR = last_len;
|
|
|
|
/* Start DMA transfer for RX */
|
|
cfg->regs->TWI_PTCR = TWI_PTCR_RXTEN;
|
|
|
|
/* Enable RX related interrupts
|
|
* RXRDY is used by PDC so we don't want to interfere.
|
|
*/
|
|
cfg->regs->TWI_IER = TWI_SR_ENDRX | TWI_SR_NACK | TWI_SR_OVRE;
|
|
|
|
/* Wait till transfer is done or error occurs */
|
|
k_sem_take(&dev_data->device_sync_sem, K_FOREVER);
|
|
|
|
/* Check for errors */
|
|
stat_reg = cfg->regs->TWI_SR;
|
|
if (stat_reg & TWI_SR_NACK) {
|
|
return RET_NACK;
|
|
}
|
|
|
|
if (stat_reg & TWI_SR_OVRE) {
|
|
return RET_ERR;
|
|
}
|
|
|
|
/* no more bytes to send */
|
|
if (dev_data->xfr_len == 0) {
|
|
break;
|
|
}
|
|
|
|
dev_data->xfr_buf += last_len;
|
|
dev_data->xfr_len -= last_len;
|
|
}
|
|
|
|
/* Disable PDC */
|
|
cfg->regs->TWI_PTCR = TWI_PTCR_RXTDIS;
|
|
|
|
/* TXCOMP is kind of misleading here. This bit is set when THR/RHR
|
|
* and all shift registers are empty, and STOP (or NACK) is detected.
|
|
* So we wait here.
|
|
*/
|
|
sr_bits_set_wait(dev, TWI_SR_TXCOMP);
|
|
|
|
return RET_OK;
|
|
}
|
|
|
|
static int i2c_sam3_transfer(struct device *dev,
|
|
struct i2c_msg *msgs, u8_t num_msgs,
|
|
u16_t slave_address)
|
|
{
|
|
const struct i2c_sam3_dev_config *const cfg = dev->config->config_info;
|
|
struct i2c_sam3_dev_data * const dev_data = dev->driver_data;
|
|
struct i2c_msg *cur_msg = msgs;
|
|
u8_t msg_left = num_msgs;
|
|
u32_t pflags = 0;
|
|
int ret = 0;
|
|
int xfr_ret;
|
|
|
|
__ASSERT_NO_MSG(msgs);
|
|
if (!num_msgs) {
|
|
return 0;
|
|
}
|
|
|
|
/* Device is busy servicing another transfer */
|
|
if (dev_data->state & STATE_BUSY) {
|
|
return -EIO;
|
|
}
|
|
|
|
dev_data->state = STATE_BUSY;
|
|
|
|
/* Need to clear status from previous transfers */
|
|
status_reg_clear(dev);
|
|
|
|
/* Enable master */
|
|
cfg->regs->TWI_CR = TWI_CR_MSEN | TWI_CR_SVDIS;
|
|
|
|
transfer_setup(dev, slave_address);
|
|
|
|
/* Process all messages one-by-one */
|
|
while (msg_left > 0) {
|
|
dev_data->xfr_buf = cur_msg->buf;
|
|
dev_data->xfr_len = cur_msg->len;
|
|
dev_data->xfr_flags = cur_msg->flags;
|
|
|
|
/* Send STOP if this is the last message */
|
|
if (msg_left == 1) {
|
|
dev_data->xfr_flags |= I2C_MSG_STOP;
|
|
}
|
|
|
|
/* The controller does not have a documented way to
|
|
* issue RESTART when changing transfer direction as master.
|
|
*
|
|
* Datasheet said about using the internal address register
|
|
* (IADR) to write 3 bytes before reading. This limits
|
|
* the number of bytes to write before a read. Also,
|
|
* this was documented under 7-bit addressing, and nothing
|
|
* about this with 10-bit addressing.
|
|
*
|
|
* Experiments show that STOP has to be issued or
|
|
* the controller hangs forever.
|
|
*/
|
|
if (msg_left > 1) {
|
|
if ((dev_data->xfr_flags & I2C_MSG_RW_MASK) !=
|
|
(cur_msg[1].flags & I2C_MSG_RW_MASK)) {
|
|
dev_data->xfr_flags |= I2C_MSG_STOP;
|
|
}
|
|
}
|
|
|
|
/* The RESTART flag is used to indicate whether to set
|
|
* the START bit in control register. This is used only
|
|
* when changing from write to read, as the START needs
|
|
* to be set to start receiving. This is also to avoid
|
|
* setting the START bit multiple time if we are doing
|
|
* multiple read messages in a roll.
|
|
*/
|
|
if ((dev_data->xfr_flags & I2C_MSG_RW_MASK) !=
|
|
(pflags & I2C_MSG_RW_MASK)) {
|
|
dev_data->xfr_flags |= I2C_MSG_RESTART;
|
|
}
|
|
|
|
dev_data->state &= ~(STATE_TX | STATE_RX);
|
|
|
|
if ((dev_data->xfr_flags & I2C_MSG_RW_MASK) == I2C_MSG_WRITE) {
|
|
dev_data->state |= STATE_TX;
|
|
xfr_ret = msg_write(dev);
|
|
} else {
|
|
dev_data->state |= STATE_RX;
|
|
xfr_ret = msg_read(dev);
|
|
}
|
|
|
|
if (xfr_ret == RET_NACK) {
|
|
/* Disable PDC if NACK is received. */
|
|
cfg->regs->TWI_PTCR = TWI_PTCR_TXTDIS
|
|
| TWI_PTCR_RXTDIS;
|
|
|
|
ret = -EIO;
|
|
goto done;
|
|
}
|
|
|
|
if (xfr_ret == RET_ERR) {
|
|
/* Error encountered:
|
|
* Reset the controller and configure it again.
|
|
*/
|
|
cfg->regs->TWI_PTCR = TWI_PTCR_TXTDIS
|
|
| TWI_PTCR_RXTDIS;
|
|
cfg->regs->TWI_CR = TWI_CR_SWRST | TWI_CR_MSDIS
|
|
| TWI_CR_SVDIS;
|
|
|
|
i2c_sam3_runtime_configure(dev,
|
|
dev_data->dev_config);
|
|
|
|
ret = -EIO;
|
|
goto done;
|
|
}
|
|
|
|
cur_msg++;
|
|
msg_left--;
|
|
pflags = cur_msg->flags;
|
|
}
|
|
|
|
done:
|
|
dev_data->state = STATE_READY;
|
|
|
|
/* Disable master and slave after transfer is done */
|
|
cfg->regs->TWI_CR = TWI_CR_MSDIS | TWI_CR_SVDIS;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static const struct i2c_driver_api api_funcs = {
|
|
.configure = i2c_sam3_runtime_configure,
|
|
.transfer = i2c_sam3_transfer,
|
|
};
|
|
|
|
static int __deprecated i2c_sam3_init(struct device *dev)
|
|
{
|
|
const struct i2c_sam3_dev_config * const cfg = dev->config->config_info;
|
|
struct i2c_sam3_dev_data * const dev_data = dev->driver_data;
|
|
|
|
k_sem_init(&dev_data->device_sync_sem, 0, UINT_MAX);
|
|
|
|
/* Disable all interrupts */
|
|
cfg->regs->TWI_IDR = cfg->regs->TWI_IMR;
|
|
|
|
cfg->config_func(dev);
|
|
|
|
if (i2c_sam3_runtime_configure(dev, dev_data->dev_config)
|
|
!= 0) {
|
|
SYS_LOG_DBG("I2C: Cannot set default configuration 0x%x",
|
|
dev_data->dev_config);
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef CONFIG_I2C_0
|
|
|
|
static void config_func_0(struct device *dev);
|
|
|
|
static const struct i2c_sam3_dev_config dev_config_0 = {
|
|
.regs = TWI0,
|
|
.config_func = config_func_0,
|
|
};
|
|
|
|
static struct i2c_sam3_dev_data dev_data_0 = {
|
|
.dev_config = CONFIG_I2C_0_DEFAULT_CFG,
|
|
};
|
|
|
|
DEVICE_AND_API_INIT(i2c_sam3_0, CONFIG_I2C_0_NAME, &i2c_sam3_init,
|
|
&dev_data_0, &dev_config_0,
|
|
POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
|
|
&api_funcs);
|
|
|
|
static void config_func_0(struct device *dev)
|
|
{
|
|
/* Enable clock for TWI0 controller */
|
|
PMC->PMC_PCER0 = (1 << ID_TWI0);
|
|
|
|
IRQ_CONNECT(TWI0_IRQn, CONFIG_I2C_0_IRQ_PRI,
|
|
i2c_sam3_isr, DEVICE_GET(i2c_sam3_0), 0);
|
|
irq_enable(TWI0_IRQn);
|
|
}
|
|
|
|
#endif /* CONFIG_I2C_0 */
|
|
|
|
#ifdef CONFIG_I2C_1
|
|
|
|
static void config_func_1(struct device *dev);
|
|
|
|
static const struct i2c_sam3_dev_config dev_config_1 = {
|
|
.regs = TWI1,
|
|
.config_func = config_func_1,
|
|
};
|
|
|
|
static struct i2c_sam3_dev_data dev_data_1 = {
|
|
.dev_config = CONFIG_I2C_1_DEFAULT_CFG,
|
|
};
|
|
|
|
DEVICE_AND_API_INIT(i2c_sam3_1, CONFIG_I2C_1_NAME, &i2c_sam3_init,
|
|
&dev_data_1, &dev_config_1,
|
|
POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
|
|
&api_funcs);
|
|
|
|
static void config_func_1(struct device *dev)
|
|
{
|
|
/* Enable clock for TWI0 controller */
|
|
PMC->PMC_PCER0 = (1 << ID_TWI1);
|
|
|
|
IRQ_CONNECT(TWI1_IRQn, CONFIG_I2C_1_IRQ_PRI,
|
|
i2c_sam3_isr, DEVICE_GET(i2c_sam3_1), 0);
|
|
irq_enable(TWI1_IRQn);
|
|
}
|
|
|
|
#endif /* CONFIG_I2C_1 */
|