533 lines
14 KiB
C
533 lines
14 KiB
C
|
/* Copyright 2013 The Chromium OS Authors. All rights reserved.
|
||
|
* Use of this source code is governed by a BSD-style license that can be
|
||
|
* found in the LICENSE file.
|
||
|
*/
|
||
|
|
||
|
/* I2C port module for MEC1322 */
|
||
|
|
||
|
#include "common.h"
|
||
|
#include "console.h"
|
||
|
#include "gpio.h"
|
||
|
#include "hooks.h"
|
||
|
#include "i2c.h"
|
||
|
#include "registers.h"
|
||
|
#include "task.h"
|
||
|
#include "timer.h"
|
||
|
#include "util.h"
|
||
|
|
||
|
#define CPUTS(outstr) cputs(CC_I2C, outstr)
|
||
|
#define CPRINTS(format, args...) cprints(CC_I2C, format, ## args)
|
||
|
|
||
|
#define I2C_CLOCK 16000000 /* 16 MHz */
|
||
|
|
||
|
/* Status */
|
||
|
#define STS_NBB BIT(0) /* Bus busy */
|
||
|
#define STS_LAB BIT(1) /* Arbitration lost */
|
||
|
#define STS_LRB BIT(3) /* Last received bit */
|
||
|
#define STS_BER BIT(4) /* Bus error */
|
||
|
#define STS_PIN BIT(7) /* Pending interrupt */
|
||
|
|
||
|
/* Control */
|
||
|
#define CTRL_ACK BIT(0) /* Acknowledge */
|
||
|
#define CTRL_STO BIT(1) /* STOP */
|
||
|
#define CTRL_STA BIT(2) /* START */
|
||
|
#define CTRL_ENI BIT(3) /* Enable interrupt */
|
||
|
#define CTRL_ESO BIT(6) /* Enable serial output */
|
||
|
#define CTRL_PIN BIT(7) /* Pending interrupt not */
|
||
|
|
||
|
/* Completion */
|
||
|
#define COMP_IDLE BIT(29) /* i2c bus is idle */
|
||
|
#define COMP_RW_BITS_MASK 0x3C /* R/W bits mask */
|
||
|
|
||
|
/* Maximum transfer of a SMBUS block transfer */
|
||
|
#define SMBUS_MAX_BLOCK_SIZE 32
|
||
|
|
||
|
/*
|
||
|
* Amount of time to blocking wait for i2c bus to finish. After this blocking
|
||
|
* timeout, if the bus is still not finished, then allow other tasks to run.
|
||
|
* Note: this is just long enough for a 400kHz bus to finish transmitting one
|
||
|
* byte assuming the bus isn't being held.
|
||
|
*/
|
||
|
#define I2C_WAIT_BLOCKING_TIMEOUT_US 25
|
||
|
|
||
|
enum i2c_transaction_state {
|
||
|
/* Stop condition was sent in previous transaction */
|
||
|
I2C_TRANSACTION_STOPPED,
|
||
|
/* Stop condition was not sent in previous transaction */
|
||
|
I2C_TRANSACTION_OPEN,
|
||
|
};
|
||
|
|
||
|
/* I2C controller state data */
|
||
|
static struct {
|
||
|
/* Transaction timeout, or 0 to use default. */
|
||
|
uint32_t timeout_us;
|
||
|
/* Task waiting on port, or TASK_ID_INVALID if none. */
|
||
|
volatile task_id_t task_waiting;
|
||
|
enum i2c_transaction_state transaction_state;
|
||
|
} cdata[I2C_CONTROLLER_COUNT];
|
||
|
|
||
|
/* Map port number to port name in datasheet, for debug prints. */
|
||
|
static const char *i2c_port_names[MEC1322_I2C_PORT_COUNT] = {
|
||
|
[MEC1322_I2C0_0] = "0_0",
|
||
|
[MEC1322_I2C0_1] = "0_1",
|
||
|
[MEC1322_I2C1] = "1",
|
||
|
[MEC1322_I2C2] = "2",
|
||
|
[MEC1322_I2C3] = "3",
|
||
|
};
|
||
|
|
||
|
static void configure_controller_speed(int controller, int kbps)
|
||
|
{
|
||
|
int t_low, t_high;
|
||
|
const int period = I2C_CLOCK / 1000 / kbps;
|
||
|
|
||
|
/*
|
||
|
* Refer to NXP UM10204 for minimum timing requirement of T_Low and
|
||
|
* T_High.
|
||
|
* http://www.nxp.com/documents/user_manual/UM10204.pdf
|
||
|
*/
|
||
|
if (kbps > 400) {
|
||
|
/* Fast mode plus */
|
||
|
t_low = t_high = period / 2 - 1;
|
||
|
MEC1322_I2C_DATA_TIM(controller) = 0x06060601;
|
||
|
MEC1322_I2C_DATA_TIM_2(controller) = 0x06;
|
||
|
} else if (kbps > 100) {
|
||
|
/* Fast mode */
|
||
|
/* By spec, clk low period is 1.3us min */
|
||
|
t_low = MAX((int)(I2C_CLOCK * 1.3 / 1000000), period / 2 - 1);
|
||
|
t_high = period - t_low - 2;
|
||
|
MEC1322_I2C_DATA_TIM(controller) = 0x040a0a01;
|
||
|
MEC1322_I2C_DATA_TIM_2(controller) = 0x0a;
|
||
|
} else {
|
||
|
/* Standard mode */
|
||
|
t_low = t_high = period / 2 - 1;
|
||
|
MEC1322_I2C_DATA_TIM(controller) = 0x0c4d5006;
|
||
|
MEC1322_I2C_DATA_TIM_2(controller) = 0x4d;
|
||
|
}
|
||
|
|
||
|
/* Clock periods is one greater than the contents of these fields */
|
||
|
MEC1322_I2C_BUS_CLK(controller) = ((t_high & 0xff) << 8) |
|
||
|
(t_low & 0xff);
|
||
|
}
|
||
|
|
||
|
static void configure_controller(int controller, int kbps)
|
||
|
{
|
||
|
MEC1322_I2C_CTRL(controller) = CTRL_PIN;
|
||
|
MEC1322_I2C_OWN_ADDR(controller) = 0x0;
|
||
|
configure_controller_speed(controller, kbps);
|
||
|
MEC1322_I2C_CTRL(controller) = CTRL_PIN | CTRL_ESO |
|
||
|
CTRL_ACK | CTRL_ENI;
|
||
|
MEC1322_I2C_CONFIG(controller) |= BIT(10); /* ENAB */
|
||
|
|
||
|
/* Enable interrupt */
|
||
|
MEC1322_I2C_CONFIG(controller) |= BIT(29); /* ENIDI */
|
||
|
MEC1322_INT_ENABLE(12) |= BIT(controller);
|
||
|
MEC1322_INT_BLK_EN |= BIT(12);
|
||
|
}
|
||
|
|
||
|
static void reset_controller(int controller)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
MEC1322_I2C_CONFIG(controller) |= BIT(9);
|
||
|
udelay(100);
|
||
|
MEC1322_I2C_CONFIG(controller) &= ~BIT(9);
|
||
|
|
||
|
for (i = 0; i < i2c_ports_used; ++i)
|
||
|
if (controller == i2c_port_to_controller(i2c_ports[i].port)) {
|
||
|
configure_controller(controller, i2c_ports[i].kbps);
|
||
|
cdata[controller].transaction_state =
|
||
|
I2C_TRANSACTION_STOPPED;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static int wait_for_interrupt(int controller, int timeout)
|
||
|
{
|
||
|
int event;
|
||
|
|
||
|
if (timeout <= 0)
|
||
|
return EC_ERROR_TIMEOUT;
|
||
|
|
||
|
cdata[controller].task_waiting = task_get_current();
|
||
|
task_enable_irq(MEC1322_IRQ_I2C_0 + controller);
|
||
|
|
||
|
/* Wait until I2C interrupt or timeout. */
|
||
|
event = task_wait_event_mask(TASK_EVENT_I2C_IDLE, timeout);
|
||
|
|
||
|
task_disable_irq(MEC1322_IRQ_I2C_0 + controller);
|
||
|
cdata[controller].task_waiting = TASK_ID_INVALID;
|
||
|
|
||
|
return (event & TASK_EVENT_TIMER) ? EC_ERROR_TIMEOUT : EC_SUCCESS;
|
||
|
}
|
||
|
|
||
|
static int wait_idle(int controller)
|
||
|
{
|
||
|
uint8_t sts = MEC1322_I2C_STATUS(controller);
|
||
|
uint64_t block_timeout = get_time().val + I2C_WAIT_BLOCKING_TIMEOUT_US;
|
||
|
uint64_t task_timeout = block_timeout + cdata[controller].timeout_us;
|
||
|
int rv = 0;
|
||
|
|
||
|
while (!(sts & STS_NBB)) {
|
||
|
if (rv)
|
||
|
return rv;
|
||
|
if (get_time().val > block_timeout)
|
||
|
rv = wait_for_interrupt(controller,
|
||
|
task_timeout - get_time().val);
|
||
|
sts = MEC1322_I2C_STATUS(controller);
|
||
|
}
|
||
|
|
||
|
if (sts & (STS_BER | STS_LAB))
|
||
|
return EC_ERROR_UNKNOWN;
|
||
|
return EC_SUCCESS;
|
||
|
}
|
||
|
|
||
|
static int wait_byte_done(int controller)
|
||
|
{
|
||
|
uint8_t sts = MEC1322_I2C_STATUS(controller);
|
||
|
uint64_t block_timeout = get_time().val + I2C_WAIT_BLOCKING_TIMEOUT_US;
|
||
|
uint64_t task_timeout = block_timeout + cdata[controller].timeout_us;
|
||
|
int rv = 0;
|
||
|
|
||
|
while (sts & STS_PIN) {
|
||
|
if (rv)
|
||
|
return rv;
|
||
|
if (get_time().val > block_timeout)
|
||
|
rv = wait_for_interrupt(controller,
|
||
|
task_timeout - get_time().val);
|
||
|
sts = MEC1322_I2C_STATUS(controller);
|
||
|
}
|
||
|
|
||
|
return sts & STS_LRB;
|
||
|
}
|
||
|
|
||
|
static void select_port(int port)
|
||
|
{
|
||
|
/*
|
||
|
* I2C0_1 uses port 1 of controller 0. All other I2C pin sets
|
||
|
* use port 0.
|
||
|
*/
|
||
|
uint8_t port_sel = (port == MEC1322_I2C0_1) ? 1 : 0;
|
||
|
int controller = i2c_port_to_controller(port);
|
||
|
|
||
|
MEC1322_I2C_CONFIG(controller) &= ~0xf;
|
||
|
MEC1322_I2C_CONFIG(controller) |= port_sel;
|
||
|
|
||
|
}
|
||
|
|
||
|
static inline int get_line_level(int controller)
|
||
|
{
|
||
|
int ret, ctrl;
|
||
|
/*
|
||
|
* We need to enable BB (Bit Bang) mode in order to read line level
|
||
|
* properly, othervise line levels return always idle (0x60).
|
||
|
*/
|
||
|
ctrl = MEC1322_I2C_BB_CTRL(controller);
|
||
|
MEC1322_I2C_BB_CTRL(controller) |= 1;
|
||
|
ret = (MEC1322_I2C_BB_CTRL(controller) >> 5) & 0x3;
|
||
|
MEC1322_I2C_BB_CTRL(controller) = ctrl;
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static inline void push_in_buf(uint8_t **in, uint8_t val, int skip)
|
||
|
{
|
||
|
if (!skip) {
|
||
|
**in = val;
|
||
|
(*in)++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int chip_i2c_xfer(const int port,
|
||
|
const uint16_t slave_addr_flags,
|
||
|
const uint8_t *out, int out_size,
|
||
|
uint8_t *in, int in_size, int flags)
|
||
|
{
|
||
|
int i;
|
||
|
int controller;
|
||
|
int send_start = flags & I2C_XFER_START;
|
||
|
int send_stop = flags & I2C_XFER_STOP;
|
||
|
int skip = 0;
|
||
|
int bytes_to_read;
|
||
|
uint8_t reg;
|
||
|
int ret_done;
|
||
|
|
||
|
if (out_size == 0 && in_size == 0)
|
||
|
return EC_SUCCESS;
|
||
|
|
||
|
select_port(port);
|
||
|
controller = i2c_port_to_controller(port);
|
||
|
if (send_start &&
|
||
|
cdata[controller].transaction_state == I2C_TRANSACTION_STOPPED)
|
||
|
wait_idle(controller);
|
||
|
|
||
|
reg = MEC1322_I2C_STATUS(controller);
|
||
|
if (send_start &&
|
||
|
cdata[controller].transaction_state == I2C_TRANSACTION_STOPPED &&
|
||
|
(((reg & (STS_BER | STS_LAB)) || !(reg & STS_NBB)) ||
|
||
|
(get_line_level(controller)
|
||
|
!= I2C_LINE_IDLE))) {
|
||
|
CPRINTS("i2c%s bad status 0x%02x, SCL=%d, SDA=%d",
|
||
|
i2c_port_names[port], reg,
|
||
|
get_line_level(controller) & I2C_LINE_SCL_HIGH,
|
||
|
get_line_level(controller) & I2C_LINE_SDA_HIGH);
|
||
|
|
||
|
/* Attempt to unwedge the port. */
|
||
|
i2c_unwedge(port);
|
||
|
|
||
|
/* Bus error, bus busy, or arbitration lost. Try reset. */
|
||
|
reset_controller(controller);
|
||
|
select_port(port);
|
||
|
|
||
|
/*
|
||
|
* We don't know what edges the slave saw, so sleep long enough
|
||
|
* that the slave will see the new start condition below.
|
||
|
*/
|
||
|
usleep(1000);
|
||
|
}
|
||
|
|
||
|
if (out_size) {
|
||
|
if (send_start) {
|
||
|
MEC1322_I2C_DATA(controller) =
|
||
|
(uint8_t)(I2C_GET_ADDR(slave_addr_flags)
|
||
|
<< 1);
|
||
|
|
||
|
/* Clock out the slave address, sending START bit */
|
||
|
MEC1322_I2C_CTRL(controller) = CTRL_PIN | CTRL_ESO |
|
||
|
CTRL_ENI | CTRL_ACK |
|
||
|
CTRL_STA;
|
||
|
cdata[controller].transaction_state =
|
||
|
I2C_TRANSACTION_OPEN;
|
||
|
}
|
||
|
|
||
|
for (i = 0; i < out_size; ++i) {
|
||
|
ret_done = wait_byte_done(controller);
|
||
|
if (ret_done)
|
||
|
goto err_chip_i2c_xfer;
|
||
|
MEC1322_I2C_DATA(controller) = out[i];
|
||
|
}
|
||
|
ret_done = wait_byte_done(controller);
|
||
|
if (ret_done)
|
||
|
goto err_chip_i2c_xfer;
|
||
|
|
||
|
/*
|
||
|
* Send STOP bit if the stop flag is on, and caller
|
||
|
* doesn't expect to receive data.
|
||
|
*/
|
||
|
if (send_stop && in_size == 0) {
|
||
|
MEC1322_I2C_CTRL(controller) = CTRL_PIN | CTRL_ESO |
|
||
|
CTRL_STO | CTRL_ACK;
|
||
|
cdata[controller].transaction_state =
|
||
|
I2C_TRANSACTION_STOPPED;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (in_size) {
|
||
|
/* Resend start bit when changing direction */
|
||
|
if (out_size || send_start) {
|
||
|
/* Repeated start case */
|
||
|
if (cdata[controller].transaction_state ==
|
||
|
I2C_TRANSACTION_OPEN)
|
||
|
MEC1322_I2C_CTRL(controller) = CTRL_ESO |
|
||
|
CTRL_STA |
|
||
|
CTRL_ACK |
|
||
|
CTRL_ENI;
|
||
|
|
||
|
MEC1322_I2C_DATA(controller) =
|
||
|
(uint8_t)(I2C_GET_ADDR(slave_addr_flags)
|
||
|
<< 1)
|
||
|
| 0x01;
|
||
|
|
||
|
/* New transaction case, clock out slave address. */
|
||
|
if (cdata[controller].transaction_state ==
|
||
|
I2C_TRANSACTION_STOPPED)
|
||
|
MEC1322_I2C_CTRL(controller) = CTRL_ESO |
|
||
|
CTRL_STA |
|
||
|
CTRL_ACK |
|
||
|
CTRL_ENI |
|
||
|
CTRL_PIN;
|
||
|
|
||
|
cdata[controller].transaction_state =
|
||
|
I2C_TRANSACTION_OPEN;
|
||
|
|
||
|
/* Skip over the dummy byte */
|
||
|
skip = 1;
|
||
|
in_size++;
|
||
|
}
|
||
|
|
||
|
/* Special flags need to be set for last two bytes */
|
||
|
bytes_to_read = send_stop ? in_size - 2 : in_size;
|
||
|
|
||
|
for (i = 0; i < bytes_to_read; ++i) {
|
||
|
ret_done = wait_byte_done(controller);
|
||
|
if (ret_done)
|
||
|
goto err_chip_i2c_xfer;
|
||
|
push_in_buf(&in, MEC1322_I2C_DATA(controller), skip);
|
||
|
skip = 0;
|
||
|
}
|
||
|
ret_done = wait_byte_done(controller);
|
||
|
if (ret_done)
|
||
|
goto err_chip_i2c_xfer;
|
||
|
|
||
|
if (send_stop) {
|
||
|
/*
|
||
|
* De-assert ACK bit before reading the next to last
|
||
|
* byte, so that the last byte is NACK'ed.
|
||
|
*/
|
||
|
MEC1322_I2C_CTRL(controller) = CTRL_ESO | CTRL_ENI;
|
||
|
push_in_buf(&in, MEC1322_I2C_DATA(controller), skip);
|
||
|
ret_done = wait_byte_done(controller);
|
||
|
if (ret_done)
|
||
|
goto err_chip_i2c_xfer;
|
||
|
|
||
|
/* Send STOP */
|
||
|
MEC1322_I2C_CTRL(controller) =
|
||
|
CTRL_PIN | CTRL_ESO | CTRL_ACK | CTRL_STO;
|
||
|
|
||
|
cdata[controller].transaction_state =
|
||
|
I2C_TRANSACTION_STOPPED;
|
||
|
|
||
|
/*
|
||
|
* We need to know our stop point two bytes in
|
||
|
* advance. If we don't know soon enough, we need
|
||
|
* to do an extra dummy read (to last_addr + 1) to
|
||
|
* issue the stop.
|
||
|
*/
|
||
|
push_in_buf(&in, MEC1322_I2C_DATA(controller),
|
||
|
in_size == 1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Check for error conditions */
|
||
|
if (MEC1322_I2C_STATUS(controller) & (STS_LAB | STS_BER))
|
||
|
return EC_ERROR_UNKNOWN;
|
||
|
|
||
|
return EC_SUCCESS;
|
||
|
err_chip_i2c_xfer:
|
||
|
/* Send STOP and return error */
|
||
|
MEC1322_I2C_CTRL(controller) = CTRL_PIN | CTRL_ESO |
|
||
|
CTRL_STO | CTRL_ACK;
|
||
|
cdata[controller].transaction_state = I2C_TRANSACTION_STOPPED;
|
||
|
if (ret_done == STS_LRB)
|
||
|
return EC_ERROR_BUSY;
|
||
|
else if (ret_done == EC_ERROR_TIMEOUT) {
|
||
|
/*
|
||
|
* If our transaction timed out then our i2c controller
|
||
|
* may be wedged without showing any other outward signs
|
||
|
* of failure. Reset the controller so that future
|
||
|
* transactions have a chance of success.
|
||
|
*/
|
||
|
reset_controller(controller);
|
||
|
return EC_ERROR_TIMEOUT;
|
||
|
}
|
||
|
else
|
||
|
return EC_ERROR_UNKNOWN;
|
||
|
}
|
||
|
|
||
|
int i2c_raw_get_scl(int port)
|
||
|
{
|
||
|
enum gpio_signal g;
|
||
|
|
||
|
/* If no SCL pin defined for this port, then return 1 to appear idle. */
|
||
|
if (get_scl_from_i2c_port(port, &g) != EC_SUCCESS)
|
||
|
return 1;
|
||
|
|
||
|
return gpio_get_level(g);
|
||
|
}
|
||
|
|
||
|
int i2c_raw_get_sda(int port)
|
||
|
{
|
||
|
enum gpio_signal g;
|
||
|
|
||
|
/* If no SDA pin defined for this port, then return 1 to appear idle. */
|
||
|
if (get_sda_from_i2c_port(port, &g) != EC_SUCCESS)
|
||
|
return 1;
|
||
|
|
||
|
return gpio_get_level(g);
|
||
|
}
|
||
|
|
||
|
int i2c_get_line_levels(int port)
|
||
|
{
|
||
|
int rv;
|
||
|
|
||
|
i2c_lock(port, 1);
|
||
|
select_port(port);
|
||
|
rv = get_line_level(i2c_port_to_controller(port));
|
||
|
i2c_lock(port, 0);
|
||
|
return rv;
|
||
|
}
|
||
|
|
||
|
int i2c_port_to_controller(int port)
|
||
|
{
|
||
|
if (port < 0 || port >= MEC1322_I2C_PORT_COUNT)
|
||
|
return -1;
|
||
|
return (port == MEC1322_I2C0_0) ? 0 : port - 1;
|
||
|
}
|
||
|
|
||
|
void i2c_set_timeout(int port, uint32_t timeout)
|
||
|
{
|
||
|
/* Param is port, but timeout is stored by-controller. */
|
||
|
cdata[i2c_port_to_controller(port)].timeout_us =
|
||
|
timeout ? timeout : I2C_TIMEOUT_DEFAULT_US;
|
||
|
}
|
||
|
|
||
|
static void i2c_init(void)
|
||
|
{
|
||
|
int i;
|
||
|
int controller;
|
||
|
int controller0_kbps = -1;
|
||
|
|
||
|
/* Configure GPIOs */
|
||
|
gpio_config_module(MODULE_I2C, 1);
|
||
|
|
||
|
for (i = 0; i < i2c_ports_used; ++i) {
|
||
|
/*
|
||
|
* If this controller has multiple ports, check if we already
|
||
|
* configured it. If so, ensure previously configured bitrate
|
||
|
* matches.
|
||
|
*/
|
||
|
controller = i2c_port_to_controller(i2c_ports[i].port);
|
||
|
if (controller == 0) {
|
||
|
if (controller0_kbps != -1) {
|
||
|
ASSERT(controller0_kbps == i2c_ports[i].kbps);
|
||
|
continue;
|
||
|
}
|
||
|
controller0_kbps = i2c_ports[i].kbps;
|
||
|
}
|
||
|
configure_controller(controller, i2c_ports[i].kbps);
|
||
|
cdata[controller].task_waiting = TASK_ID_INVALID;
|
||
|
cdata[controller].transaction_state = I2C_TRANSACTION_STOPPED;
|
||
|
|
||
|
/* Use default timeout. */
|
||
|
i2c_set_timeout(i2c_ports[i].port, 0);
|
||
|
}
|
||
|
}
|
||
|
DECLARE_HOOK(HOOK_INIT, i2c_init, HOOK_PRIO_INIT_I2C);
|
||
|
|
||
|
static void handle_interrupt(int controller)
|
||
|
{
|
||
|
int id = cdata[controller].task_waiting;
|
||
|
|
||
|
/* Clear the interrupt status */
|
||
|
MEC1322_I2C_COMPLETE(controller) &= (COMP_RW_BITS_MASK | COMP_IDLE);
|
||
|
|
||
|
/*
|
||
|
* Write to control register interferes with I2C transaction.
|
||
|
* Instead, let's disable IRQ from the core until the next time
|
||
|
* we want to wait for STS_PIN/STS_NBB.
|
||
|
*/
|
||
|
task_disable_irq(MEC1322_IRQ_I2C_0 + controller);
|
||
|
|
||
|
/* Wake up the task which was waiting on the I2C interrupt, if any. */
|
||
|
if (id != TASK_ID_INVALID)
|
||
|
task_set_event(id, TASK_EVENT_I2C_IDLE, 0);
|
||
|
}
|
||
|
|
||
|
void i2c0_interrupt(void) { handle_interrupt(0); }
|
||
|
void i2c1_interrupt(void) { handle_interrupt(1); }
|
||
|
void i2c2_interrupt(void) { handle_interrupt(2); }
|
||
|
void i2c3_interrupt(void) { handle_interrupt(3); }
|
||
|
|
||
|
DECLARE_IRQ(MEC1322_IRQ_I2C_0, i2c0_interrupt, 2);
|
||
|
DECLARE_IRQ(MEC1322_IRQ_I2C_1, i2c1_interrupt, 2);
|
||
|
DECLARE_IRQ(MEC1322_IRQ_I2C_2, i2c2_interrupt, 2);
|
||
|
DECLARE_IRQ(MEC1322_IRQ_I2C_3, i2c3_interrupt, 2);
|