/* * Copyright (c) 2015 Intel Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * @file I2C driver for Quark SE Sensor Subsystem. * * The I2C on Quark SE Sensor Subsystem is similar to DesignWare I2C IP block, * but with a different register set and different workflow. */ #include #include #include #include #include #include #include #include #include #include "i2c_quark_se_ss.h" #include "i2c_quark_se_ss_registers.h" #ifndef CONFIG_I2C_DEBUG #define DBG(...) { ; } #else #include #define DBG printk #endif /* CONFIG_I2C_DEBUG */ static inline uint32_t _i2c_qse_ss_memory_read(uint32_t base_addr, uint32_t offset) { return sys_read32(base_addr + offset); } static inline void _i2c_qse_ss_memory_write(uint32_t base_addr, uint32_t offset, uint32_t val) { sys_write32(val, base_addr + offset); } static inline uint32_t _i2c_qse_ss_reg_read(struct device *dev, uint32_t reg) { struct i2c_qse_ss_rom_config * const rom = dev->config->config_info; return _arc_v2_aux_reg_read(rom->base_address + reg); } static inline void _i2c_qse_ss_reg_write(struct device *dev, uint32_t reg, uint32_t val) { struct i2c_qse_ss_rom_config * const rom = dev->config->config_info; _arc_v2_aux_reg_write(rom->base_address + reg, val); } static inline void _i2c_qse_ss_reg_write_and(struct device *dev, uint32_t reg, uint32_t mask) { uint32_t r; r = _i2c_qse_ss_reg_read(dev, reg); r &= mask; _i2c_qse_ss_reg_write(dev, reg, r); } static inline void _i2c_qse_ss_reg_write_or(struct device *dev, uint32_t reg, uint32_t mask) { uint32_t r; r = _i2c_qse_ss_reg_read(dev, reg); r |= mask; _i2c_qse_ss_reg_write(dev, reg, r); } static inline int _i2c_qse_ss_reg_check_bit(struct device *dev, uint32_t reg, uint32_t mask) { return _i2c_qse_ss_reg_read(dev, reg) & mask; } /* Is the controller busy? */ static inline bool _i2c_qse_ss_is_busy(struct device *dev) { return _i2c_qse_ss_reg_check_bit(dev, REG_STATUS, IC_STATUS_ACTIVITY); } /* Is RX FIFO not empty? */ static inline bool _i2c_qse_ss_is_rfne(struct device *dev) { return _i2c_qse_ss_reg_check_bit(dev, REG_STATUS, IC_STATUS_RFNE); } /* Is TX FIFO not full? */ static inline bool _i2c_qse_ss_is_tfnf(struct device *dev) { return _i2c_qse_ss_reg_check_bit(dev, REG_STATUS, IC_STATUS_TFNF); } /* Is TX FIFO empty? */ static inline bool _i2c_qse_ss_is_tfe(struct device *dev) { return _i2c_qse_ss_reg_check_bit(dev, REG_STATUS, IC_STATUS_TFE); } /* Check a certain bit in the interrupt register */ static inline bool _i2c_qse_ss_check_irq(struct device *dev, uint32_t mask) { return _i2c_qse_ss_reg_check_bit(dev, REG_INTR_STAT, mask); } static inline void _i2c_qse_ss_data_ask(struct device *dev) { struct i2c_qse_ss_dev_config * const dw = dev->driver_data; uint32_t data; uint8_t tx_empty; int8_t rx_empty; uint8_t cnt; /* No more bytes to request, so command queue is no longer needed */ if (dw->request_bytes == 0) { _i2c_qse_ss_reg_write_and(dev, REG_INTR_MASK, ~(IC_INTR_TX_EMPTY)); return; } /* How many bytes we can actually ask */ rx_empty = I2C_QSE_SS_FIFO_DEPTH - _i2c_qse_ss_reg_read(dev, REG_RXFLR); rx_empty -= dw->rx_pending; if (rx_empty < 0) { /* RX FIFO expected to be full. * So don't request any bytes, yet. */ return; } /* How many empty slots in TX FIFO (as command queue) */ tx_empty = I2C_QSE_SS_FIFO_DEPTH - _i2c_qse_ss_reg_read(dev, REG_TXFLR); /* Figure out how many bytes we can request */ cnt = min(I2C_QSE_SS_FIFO_DEPTH, dw->request_bytes); cnt = min(min(tx_empty, rx_empty), cnt); while (cnt > 0) { /* Tell controller to get another byte */ data = IC_DATA_CMD_CMD | IC_DATA_CMD_STROBE | IC_DATA_CMD_POP; /* Send RESTART if needed */ if (dw->xfr_flags & I2C_MSG_RESTART) { data |= IC_DATA_CMD_RESTART; dw->xfr_flags &= ~(I2C_MSG_RESTART); } /* After receiving the last byte, send STOP if needed */ if ((dw->xfr_flags & I2C_MSG_STOP) && (dw->request_bytes == 1)) { data |= IC_DATA_CMD_STOP; } _i2c_qse_ss_reg_write(dev, REG_DATA_CMD, data); dw->rx_pending++; dw->request_bytes--; cnt--; } } static void _i2c_qse_ss_data_read(struct device *dev) { struct i2c_qse_ss_dev_config * const dw = dev->driver_data; while (_i2c_qse_ss_is_rfne(dev) && (dw->xfr_len > 0)) { /* Need to write 0 to POP bit to * "pop" one byte from RX FIFO. */ _i2c_qse_ss_reg_write(dev, REG_DATA_CMD, IC_DATA_CMD_STROBE); dw->xfr_buf[0] = _i2c_qse_ss_reg_read(dev, REG_DATA_CMD) & IC_DATA_CMD_DATA_MASK; dw->xfr_buf++; dw->xfr_len--; dw->rx_pending--; if (dw->xfr_len == 0) { break; } } /* Nothing to receive anymore */ if (dw->xfr_len == 0) { dw->state &= ~I2C_QSE_SS_CMD_RECV; return; } } static int _i2c_qse_ss_data_send(struct device *dev) { struct i2c_qse_ss_dev_config * const dw = dev->driver_data; uint32_t data; /* Nothing to send anymore, mask the interrupt */ if (dw->xfr_len == 0) { _i2c_qse_ss_reg_write_and(dev, REG_INTR_MASK, ~(IC_INTR_TX_EMPTY)); dw->state &= ~I2C_QSE_SS_CMD_SEND; return 0; } while (_i2c_qse_ss_is_tfnf(dev) && (dw->xfr_len > 0)) { /* We have something to transmit to a specific host */ data = dw->xfr_buf[0] | IC_DATA_CMD_STROBE | IC_DATA_CMD_POP; /* Send RESTART if needed */ if (dw->xfr_flags & I2C_MSG_RESTART) { data |= IC_DATA_CMD_RESTART; dw->xfr_flags &= ~(I2C_MSG_RESTART); } /* Send STOP if needed */ if ((dw->xfr_len == 1) && (dw->xfr_flags & I2C_MSG_STOP)) { data |= IC_DATA_CMD_STOP; } _i2c_qse_ss_reg_write(dev, REG_DATA_CMD, data); dw->xfr_len--; dw->xfr_buf++; if (_i2c_qse_ss_check_irq(dev, IC_INTR_TX_ABRT)) { return -EIO; } } return 0; } static inline void _i2c_qse_ss_transfer_complete(struct device *dev) { struct i2c_qse_ss_dev_config * const dw = dev->driver_data; /* Disable and clear all pending interrupts */ _i2c_qse_ss_reg_write(dev, REG_INTR_MASK, IC_INTR_MASK_ALL); _i2c_qse_ss_reg_write(dev, REG_INTR_CLR, IC_INTR_CLR_ALL); device_sync_call_complete(&dw->sync); } void i2c_qse_ss_isr(void *arg) { struct device *dev = (struct device *)arg; struct i2c_qse_ss_dev_config * const dw = dev->driver_data; uint32_t ic_intr_stat; int ret = 0; /* * Causes of an intterrupts: * - STOP condition is detected * - Transfer is aborted * - Transmit FIFO is empy * - Transmit FIFO is overflowing * - Receive FIFO is full * - Receive FIFO overflow * - Received FIFO underrun */ DBG("I2C_SS: interrupt received\n"); ic_intr_stat = _i2c_qse_ss_reg_read(dev, REG_INTR_STAT); /* Error conditions */ if ((IC_INTR_TX_ABRT | IC_INTR_TX_OVER | IC_INTR_RX_OVER | IC_INTR_RX_UNDER) & ic_intr_stat) { dw->state = I2C_QSE_SS_CMD_ERROR; goto done; } /* Check if the RX FIFO reached threshold */ if (ic_intr_stat & IC_INTR_RX_FULL) { _i2c_qse_ss_data_read(dev); _i2c_qse_ss_reg_write(dev, REG_INTR_CLR, IC_INTR_RX_FULL); } /* Check if the TX FIFO is ready for commands. * TX FIFO also serves as command queue where read requests * are written to TX FIFO. */ if (ic_intr_stat & IC_INTR_TX_EMPTY) { if ((dw->xfr_flags & I2C_MSG_RW_MASK) == I2C_MSG_WRITE) { ret = _i2c_qse_ss_data_send(dev); } else { _i2c_qse_ss_data_ask(dev); } _i2c_qse_ss_reg_write(dev, REG_INTR_CLR, IC_INTR_TX_EMPTY); /* If STOP is not expected, finish processing this * message if there is nothing left to do anymore. * Or bail if there is any error. */ if (((dw->xfr_len == 0) && !(dw->xfr_flags & I2C_MSG_STOP)) || (ret != 0)) { goto done; } } /* STOP detected */ if (ic_intr_stat & IC_INTR_STOP_DET) { _i2c_qse_ss_reg_write(dev, REG_INTR_CLR, IC_INTR_STOP_DET); goto done; } return; done: _i2c_qse_ss_transfer_complete(dev); } static int _i2c_qse_ss_setup(struct device *dev, uint16_t addr) { struct i2c_qse_ss_dev_config * const dw = dev->driver_data; uint32_t ic_con; int rc = 0; /* Disable the device controller but enable clock * so we can setup the controller. */ _i2c_qse_ss_reg_write_and(dev, REG_CON, ~(IC_CON_ENABLE)); /* Disable and clear all pending interrupts */ _i2c_qse_ss_reg_write(dev, REG_INTR_MASK, IC_INTR_MASK_ALL); _i2c_qse_ss_reg_write(dev, REG_INTR_CLR, IC_INTR_CLR_ALL); ic_con = _i2c_qse_ss_reg_read(dev, REG_CON); ic_con &= IC_CON_SPKLEN_MASK; ic_con |= IC_CON_RESTART_EN | IC_CON_CLK_ENA; /* Set addressing mode - (initialization = 7 bit) */ if (dw->app_config.bits.use_10_bit_addr) { DBG("I2C: using 10-bit address\n"); ic_con |= IC_CON_10BIT_ADDR; } /* Setup the clock frequency and speed mode */ switch (dw->app_config.bits.speed) { case I2C_SPEED_STANDARD: DBG("I2C: speed set to STANDARD\n"); _i2c_qse_ss_reg_write(dev, REG_SS_SCL_CNT, (dw->hcnt << 16) | (dw->lcnt & 0xFFFF)); ic_con |= I2C_QSE_SS_SPEED_STANDARD << IC_CON_SPEED_POS; break; case I2C_SPEED_FAST: /* fall through */ case I2C_SPEED_FAST_PLUS: DBG("I2C: speed set to FAST or FAST_PLUS\n"); _i2c_qse_ss_reg_write(dev, REG_FS_SCL_CNT, (dw->hcnt << 16) | (dw->lcnt & 0xFFFF)); ic_con |= I2C_QSE_SS_SPEED_FAST << IC_CON_SPEED_POS; break; default: DBG("I2C: invalid speed requested\n"); /* TODO change */ rc = -EINVAL; goto done; } /* Set the target address */ ic_con |= addr << IC_CON_TAR_SAR_POS; _i2c_qse_ss_reg_write(dev, REG_CON, ic_con); /* Set TX/RX fifo threshold level. * * RX: * Setting it to 1 so RX_FULL is set whenever there is * data in RX FIFO. (actual value is reg value +1) * * TX: * Setting it to 0 so TX_EMPTY is set only when * TX FIFO is truly empty. So that we can let * the controller do the transfers for longer period * before we need to fill the FIFO again. This may * cause some pauses during transfers, but this keeps * the device from interrupting often. * * TODO: extend the threshold for multi-byte RX FIFO. */ _i2c_qse_ss_reg_write(dev, REG_TL, 0x00000000); /* SDA Hold time has to setup to minimal 2 according to spec. */ _i2c_qse_ss_reg_write(dev, REG_SDA_CONFIG, 0x00020000); done: return rc; } static int i2c_qse_ss_intr_transfer(struct device *dev, struct i2c_msg *msgs, uint8_t num_msgs, uint16_t slave_address) { struct i2c_qse_ss_dev_config * const dw = dev->driver_data; struct i2c_msg *cur_msg = msgs; uint8_t msg_left = num_msgs; uint8_t pflags; int ret; /* Why bother processing no messages */ if (!msgs || !num_msgs) { return -ENOTSUP; } /* First step, check if device is idle */ if (_i2c_qse_ss_is_busy(dev) || (dw->state & I2C_QSE_SS_BUSY)) { return -EBUSY; } dw->state |= I2C_QSE_SS_BUSY; ret = _i2c_qse_ss_setup(dev, slave_address); if (ret) { dw->state = I2C_QSE_SS_STATE_READY; return ret; } /* To prevent RESTART for first message */ dw->xfr_flags = msgs[0].flags; /* Enable controller */ _i2c_qse_ss_reg_write_or(dev, REG_CON, IC_CON_ENABLE); /* Process all the messages */ while (msg_left > 0) { pflags = dw->xfr_flags; dw->xfr_buf = cur_msg->buf; dw->xfr_len = cur_msg->len; dw->xfr_flags = cur_msg->flags; dw->rx_pending = 0; /* Need to RESTART if changing transfer direction */ if ((pflags & I2C_MSG_RW_MASK) != (dw->xfr_flags & I2C_MSG_RW_MASK)) { dw->xfr_flags |= I2C_MSG_RESTART; } /* Send STOP if this is the last message */ if (msg_left == 1) { dw->xfr_flags |= I2C_MSG_STOP; } dw->state &= ~(I2C_QSE_SS_CMD_SEND | I2C_QSE_SS_CMD_RECV); if ((dw->xfr_flags & I2C_MSG_RW_MASK) == I2C_MSG_WRITE) { dw->state |= I2C_QSE_SS_CMD_SEND; dw->request_bytes = 0; } else { dw->state |= I2C_QSE_SS_CMD_RECV; dw->request_bytes = dw->xfr_len; } /* Enable interrupts to trigger ISR */ _i2c_qse_ss_reg_write(dev, REG_INTR_MASK, (IC_INTR_MASK_TX | IC_INTR_MASK_RX)); /* Wait for transfer to be done */ device_sync_call_wait(&dw->sync); if (dw->state & I2C_QSE_SS_CMD_ERROR) { ret = -EIO; break; } /* Something wrong if there is something left to do */ if (dw->xfr_len > 0) { ret = -EIO; break; } cur_msg++; msg_left--; } dw->state = I2C_QSE_SS_STATE_READY; return ret; } static int i2c_qse_ss_runtime_configure(struct device *dev, uint32_t config) { struct i2c_qse_ss_dev_config * const dw = dev->driver_data; uint32_t value = 0; uint32_t rc = 0; uint32_t ic_con; uint32_t spklen; dw->app_config.raw = config; ic_con = _i2c_qse_ss_reg_read(dev, REG_CON); spklen = (ic_con & IC_CON_SPKLEN_MASK) >> IC_CON_SPKLEN_POS; /* Make sure we have a supported speed for the DesignWare model */ /* and have setup the clock frequency and speed mode */ switch (dw->app_config.bits.speed) { case I2C_SPEED_STANDARD: /* Following the directions on DW spec page 59, IC_SS_SCL_LCNT * must have register values larger than IC_FS_SPKLEN + 7 */ if (I2C_STD_LCNT <= (spklen + 7)) { value = spklen + 8; } else { value = I2C_STD_LCNT; } dw->lcnt = value; /* Following the directions on DW spec page 59, IC_SS_SCL_HCNT * must have register values larger than IC_FS_SPKLEN + 5 */ if (I2C_STD_HCNT <= (spklen + 5)) { value = spklen + 6; } else { value = I2C_STD_HCNT; } dw->hcnt = value; break; case I2C_SPEED_FAST: /* fall through */ case I2C_SPEED_FAST_PLUS: /* * Following the directions on DW spec page 59, IC_FS_SCL_LCNT * must have register values larger than IC_FS_SPKLEN + 7 */ if (I2C_FS_LCNT <= (spklen + 7)) { value = spklen + 8; } else { value = I2C_FS_LCNT; } dw->lcnt = value; /* * Following the directions on DW spec page 59, IC_FS_SCL_HCNT * must have register values larger than IC_FS_SPKLEN + 5 */ if (I2C_FS_HCNT <= (spklen + 5)) { value = spklen + 6; } else { value = I2C_FS_HCNT; } dw->hcnt = value; break; default: /* TODO change */ rc = -EINVAL; } /* * Clear any interrupts currently waiting in the controller */ _i2c_qse_ss_reg_write(dev, REG_INTR_CLR, IC_INTR_CLR_ALL); return rc; } static int i2c_qse_ss_suspend(struct device *dev) { DBG("I2C_SS: suspend called - function not yet implemented\n"); /* TODO - add this code */ return 0; } static int i2c_qse_ss_resume(struct device *dev) { DBG("I2C_SS: resume called - function not yet implemented\n"); /* TODO - add this code */ return 0; } static struct i2c_driver_api ss_funcs = { .configure = i2c_qse_ss_runtime_configure, .transfer = i2c_qse_ss_intr_transfer, .suspend = i2c_qse_ss_suspend, .resume = i2c_qse_ss_resume, }; int i2c_qse_ss_initialize(struct device *dev) { struct i2c_qse_ss_rom_config * const rom = dev->config->config_info; struct i2c_qse_ss_dev_config * const dw = dev->driver_data; dev->driver_api = &ss_funcs; if (rom->config_func) { rom->config_func(dev); } /* Enable clock for controller so we can talk to it */ _i2c_qse_ss_reg_write_or(dev, REG_CON, IC_CON_CLK_ENA); device_sync_call_init(&dw->sync); if (i2c_qse_ss_runtime_configure(dev, dw->app_config.raw) != 0) { DBG("I2C_SS: Cannot set default configuration 0x%x\n", dw->app_config.raw); return -EPERM; } dw->state = I2C_QSE_SS_STATE_READY; return 0; } #if CONFIG_I2C_QUARK_SE_SS_0 #include void _i2c_qse_ss_config_irq_0(struct device *port); struct i2c_qse_ss_rom_config i2c_config_ss_0 = { .base_address = CONFIG_I2C_QUARK_SE_SS_0_BASE, .config_func = _i2c_qse_ss_config_irq_0, }; struct i2c_qse_ss_dev_config i2c_ss_0_runtime = { .app_config.raw = CONFIG_I2C_QUARK_SE_SS_0_DEFAULT_CFG, }; DEVICE_INIT(i2c_ss_0, CONFIG_I2C_QUARK_SE_SS_0_NAME, &i2c_qse_ss_initialize, &i2c_ss_0_runtime, &i2c_config_ss_0, SECONDARY, CONFIG_I2C_INIT_PRIORITY); void _i2c_qse_ss_config_irq_0(struct device *port) { uint32_t mask = 0; /* Need to unmask the interrupts in System Control Subsystem (SCSS) * so the interrupt controller can route these interrupts to * the sensor subsystem. */ mask = _i2c_qse_ss_memory_read(SCSS_REGISTER_BASE, I2C_SS_0_ERR_MASK); mask &= INT_ENABLE_ARC; _i2c_qse_ss_memory_write(SCSS_REGISTER_BASE, I2C_SS_0_ERR_MASK, mask); mask = _i2c_qse_ss_memory_read(SCSS_REGISTER_BASE, I2C_SS_0_TX_MASK); mask &= INT_ENABLE_ARC; _i2c_qse_ss_memory_write(SCSS_REGISTER_BASE, I2C_SS_0_TX_MASK, mask); mask = _i2c_qse_ss_memory_read(SCSS_REGISTER_BASE, I2C_SS_0_RX_MASK); mask &= INT_ENABLE_ARC; _i2c_qse_ss_memory_write(SCSS_REGISTER_BASE, I2C_SS_0_RX_MASK, mask); mask = _i2c_qse_ss_memory_read(SCSS_REGISTER_BASE, I2C_SS_0_STOP_MASK); mask &= INT_ENABLE_ARC; _i2c_qse_ss_memory_write(SCSS_REGISTER_BASE, I2C_SS_0_STOP_MASK, mask); /* Connect the IRQs to ISR */ IRQ_CONNECT(I2C_SS_0_ERR_VECTOR, 1, i2c_qse_ss_isr, DEVICE_GET(i2c_ss_0), 0); IRQ_CONNECT(I2C_SS_0_RX_VECTOR, 1, i2c_qse_ss_isr, DEVICE_GET(i2c_ss_0), 0); IRQ_CONNECT(I2C_SS_0_TX_VECTOR, 1, i2c_qse_ss_isr, DEVICE_GET(i2c_ss_0), 0); IRQ_CONNECT(I2C_SS_0_STOP_VECTOR, 1, i2c_qse_ss_isr, DEVICE_GET(i2c_ss_0), 0); irq_enable(I2C_SS_0_ERR_VECTOR); irq_enable(I2C_SS_0_RX_VECTOR); irq_enable(I2C_SS_0_TX_VECTOR); irq_enable(I2C_SS_0_STOP_VECTOR); } #endif /* CONFIG_I2C_QUARK_SE_SS_0 */ #if CONFIG_I2C_QUARK_SE_SS_1 #include void _i2c_qse_ss_config_irq_1(struct device *port); struct i2c_qse_ss_rom_config i2c_config_ss_1 = { .base_address = CONFIG_I2C_QUARK_SE_SS_1_BASE, .config_func = _i2c_qse_ss_config_irq_1, }; struct i2c_qse_ss_dev_config i2c_qse_ss_1_runtime = { .app_config.raw = CONFIG_I2C_QUARK_SE_SS_1_DEFAULT_CFG, }; DEVICE_INIT(i2c_ss_1, CONFIG_I2C_QUARK_SE_SS_1_NAME, &i2c_qse_ss_initialize, &i2c_qse_ss_1_runtime, &i2c_config_ss_1, SECONDARY, CONFIG_I2C_INIT_PRIORITY); void _i2c_qse_ss_config_irq_1(struct device *port) { uint32_t mask = 0; /* Need to unmask the interrupts in System Control Subsystem (SCSS) * so the interrupt controller can route these interrupts to * the sensor subsystem. */ mask = _i2c_qse_ss_memory_read(SCSS_REGISTER_BASE, I2C_SS_1_ERR_MASK); mask &= INT_ENABLE_ARC; _i2c_qse_ss_memory_write(SCSS_REGISTER_BASE, I2C_SS_1_ERR_MASK, mask); mask = _i2c_qse_ss_memory_read(SCSS_REGISTER_BASE, I2C_SS_1_TX_MASK); mask &= INT_ENABLE_ARC; _i2c_qse_ss_memory_write(SCSS_REGISTER_BASE, I2C_SS_1_TX_MASK, mask); mask = _i2c_qse_ss_memory_read(SCSS_REGISTER_BASE, I2C_SS_1_RX_MASK); mask &= INT_ENABLE_ARC; _i2c_qse_ss_memory_write(SCSS_REGISTER_BASE, I2C_SS_1_RX_MASK, mask); mask = _i2c_qse_ss_memory_read(SCSS_REGISTER_BASE, I2C_SS_1_STOP_MASK); mask &= INT_ENABLE_ARC; _i2c_qse_ss_memory_write(SCSS_REGISTER_BASE, I2C_SS_1_STOP_MASK, mask); /* Connect the IRQs to ISR */ IRQ_CONNECT(I2C_SS_1_ERR_VECTOR, 1, i2c_qse_ss_isr, DEVICE_GET(i2c_ss_1), 0); IRQ_CONNECT(I2C_SS_1_RX_VECTOR, 1, i2c_qse_ss_isr, DEVICE_GET(i2c_ss_1), 0); IRQ_CONNECT(I2C_SS_1_TX_VECTOR, 1, i2c_qse_ss_isr, DEVICE_GET(i2c_ss_1), 0); IRQ_CONNECT(I2C_SS_1_STOP_VECTOR, 1, i2c_qse_ss_isr, DEVICE_GET(i2c_ss_1), 0); irq_enable(I2C_SS_1_ERR_VECTOR); irq_enable(I2C_SS_1_RX_VECTOR); irq_enable(I2C_SS_1_TX_VECTOR); irq_enable(I2C_SS_1_STOP_VECTOR); } #endif /* CONFIG_I2C_QUARK_SE_SS_1 */