mirror of
https://github.com/zephyrproject-rtos/zephyr
synced 2025-09-02 00:02:48 +00:00
() Make driver API into const, as it is not being modified at runtime. Saves 24 bytes of RAM by putting it into ROM. () Make device config structs as static as they are local to the source file, and should not be referenced from outside at all. () Same goes for the IRQ configuration functions, by declaring them static. Change-Id: I5225dc550bdd65ec88a8930944da063efe16a3b2 Signed-off-by: Daniel Leung <daniel.leung@intel.com>
770 lines
20 KiB
C
770 lines
20 KiB
C
/*
|
|
* 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 <errno.h>
|
|
#include <stdint.h>
|
|
#include <stdbool.h>
|
|
|
|
#include <i2c.h>
|
|
#include <nanokernel.h>
|
|
#include <arch/cpu.h>
|
|
|
|
#include <sys_io.h>
|
|
|
|
#include <board.h>
|
|
#include <misc/util.h>
|
|
|
|
#include "i2c_quark_se_ss.h"
|
|
#include "i2c_quark_se_ss_registers.h"
|
|
|
|
#ifndef CONFIG_I2C_DEBUG
|
|
#define DBG(...) { ; }
|
|
#else
|
|
#include <misc/printk.h>
|
|
#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 const struct i2c_driver_api api_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;
|
|
|
|
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 <init.h>
|
|
|
|
static void _i2c_qse_ss_config_irq_0(struct device *port);
|
|
|
|
static const struct i2c_qse_ss_rom_config i2c_config_ss_0 = {
|
|
.base_address = I2C_QUARK_SE_SS_0_BASE_ADDR,
|
|
|
|
.config_func = _i2c_qse_ss_config_irq_0,
|
|
};
|
|
|
|
static struct i2c_qse_ss_dev_config i2c_ss_0_runtime = {
|
|
.app_config.raw = CONFIG_I2C_QUARK_SE_SS_0_DEFAULT_CFG,
|
|
};
|
|
|
|
DEVICE_AND_API_INIT(i2c_ss_0, CONFIG_I2C_QUARK_SE_SS_0_NAME,
|
|
&i2c_qse_ss_initialize,
|
|
&i2c_ss_0_runtime, (void *)&i2c_config_ss_0,
|
|
SECONDARY, CONFIG_I2C_INIT_PRIORITY,
|
|
(void *)&api_funcs);
|
|
|
|
static 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 <init.h>
|
|
|
|
static void _i2c_qse_ss_config_irq_1(struct device *port);
|
|
|
|
static const struct i2c_qse_ss_rom_config i2c_config_ss_1 = {
|
|
.base_address = I2C_QUARK_SE_SS_1_BASE_ADDR,
|
|
|
|
.config_func = _i2c_qse_ss_config_irq_1,
|
|
};
|
|
|
|
static struct i2c_qse_ss_dev_config i2c_qse_ss_1_runtime = {
|
|
.app_config.raw = CONFIG_I2C_QUARK_SE_SS_1_DEFAULT_CFG,
|
|
};
|
|
|
|
DEVICE_AND_API_INIT(i2c_ss_1, CONFIG_I2C_QUARK_SE_SS_1_NAME,
|
|
&i2c_qse_ss_initialize,
|
|
&i2c_qse_ss_1_runtime, (void *)&i2c_config_ss_1,
|
|
SECONDARY, CONFIG_I2C_INIT_PRIORITY);
|
|
(void *)&api_funcs);
|
|
|
|
|
|
static 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 */
|