i2c: quark se: Add QMSI 1.1-based I2C shim driver

Add I2C QMSI shim driver for sensor system based on QMSI 1.1

Origin: Original
Change-Id: I9c8efe49e8e9b7a5f8496fa49beb68e409148be7
Signed-off-by: Sergio Rodriguez <sergio.sf.rodriguez@intel.com>
Signed-off-by: Anas Nashif <anas.nashif@intel.com>
This commit is contained in:
Sergio Rodriguez 2016-04-14 17:07:35 -07:00 committed by Anas Nashif
commit 92e9ffe481
4 changed files with 326 additions and 2 deletions

View file

@ -43,6 +43,25 @@ config I2C_QUARK_SE_SS
This option enables the driver to support the I2C on Quark SE Sensor This option enables the driver to support the I2C on Quark SE Sensor
Subsystem. Subsystem.
config I2C_QMSI_SS
bool "QMSI I2C driver for the Sensor Subsystem"
depends on I2C && QMSI
default n
help
This option enable the Sensor QMSI I2C driver.
This driver is simply a shim driver based on the I2C driver
provided by the QMSI BSP.
config I2C_QMSI_SS
bool "QMSI I2C driver for the Sensor Subsystem"
depends on QMSI && I2C
default n
help
This option enable the Sensor QMSI I2C driver.
This driver is simply a shim driver based on the I2C driver
provided by the QMSI BSP.
config I2C_QMSI config I2C_QMSI
bool "QMSI I2C driver" bool "QMSI I2C driver"

View file

@ -1,5 +1,5 @@
ccflags-$(CONFIG_I2C_QMSI) +=-I$(CONFIG_QMSI_INSTALL_PATH)/include
obj-$(CONFIG_I2C_DW) += i2c_dw.o obj-$(CONFIG_I2C_DW) += i2c_dw.o
obj-$(CONFIG_I2C_QMSI) += i2c_qmsi.o obj-$(CONFIG_I2C_QMSI) += i2c_qmsi.o
obj-$(CONFIG_I2C_QMSI_SS) += i2c_qmsi_ss.o
obj-$(CONFIG_I2C_QUARK_SE_SS) += i2c_quark_se_ss.o obj-$(CONFIG_I2C_QUARK_SE_SS) += i2c_quark_se_ss.o
obj-$(CONFIG_I2C_ATMEL_SAM3) += i2c_atmel_sam3.o obj-$(CONFIG_I2C_ATMEL_SAM3) += i2c_atmel_sam3.o

304
drivers/i2c/i2c_qmsi_ss.c Normal file
View file

@ -0,0 +1,304 @@
/*
* Copyright (c) 2016 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.
*/
#include <errno.h>
#include <device.h>
#include <i2c.h>
#include <board.h>
#include "qm_ss_i2c.h"
#include "qm_ss_isr.h"
#include "ss_clk.h"
/* Convenient macros to get the controller instance and the driver data. */
#define GET_CONTROLLER_INSTANCE(dev) \
(((struct i2c_qmsi_ss_config_info *)dev->config->config_info)->instance)
#define GET_DRIVER_DATA(dev) \
((struct i2c_qmsi_ss_driver_data *)dev->driver_data)
struct i2c_qmsi_ss_config_info {
qm_ss_i2c_t instance; /* Controller instance. */
union dev_config default_cfg;
void (*irq_cfg)(void);
};
struct i2c_qmsi_ss_driver_data {
device_sync_call_t sync;
int transfer_status;
};
static int i2c_qmsi_ss_init(struct device *dev);
static void i2c_qmsi_ss_isr(void *arg)
{
struct device *dev = arg;
qm_ss_i2c_t instance = GET_CONTROLLER_INSTANCE(dev);
if (instance == QM_SS_I2C_0) {
qm_ss_i2c_isr_0(NULL);
} else {
qm_ss_i2c_isr_1(NULL);
}
}
#ifdef CONFIG_I2C_0
static struct i2c_qmsi_ss_driver_data driver_data_0;
static void i2c_qmsi_ss_config_irq_0(void);
static struct i2c_qmsi_ss_config_info config_info_0 = {
.instance = QM_SS_I2C_0,
.default_cfg.raw = CONFIG_I2C_0_DEFAULT_CFG,
.irq_cfg = i2c_qmsi_ss_config_irq_0,
};
DEVICE_INIT(i2c_ss_0, CONFIG_I2C_0_NAME, i2c_qmsi_ss_init, &driver_data_0,
&config_info_0, SECONDARY, CONFIG_KERNEL_INIT_PRIORITY_DEVICE);
static void i2c_qmsi_ss_config_irq_0(void)
{
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 = sys_read32(SCSS_REGISTER_BASE + I2C_SS_0_ERR_MASK);
mask &= INT_ENABLE_ARC;
sys_write32(mask, SCSS_REGISTER_BASE + I2C_SS_0_ERR_MASK);
mask = sys_read32(SCSS_REGISTER_BASE + I2C_SS_0_TX_MASK);
mask &= INT_ENABLE_ARC;
sys_write32(mask, SCSS_REGISTER_BASE + I2C_SS_0_TX_MASK);
mask = sys_read32(SCSS_REGISTER_BASE + I2C_SS_0_RX_MASK);
mask &= INT_ENABLE_ARC;
sys_write32(mask, SCSS_REGISTER_BASE + I2C_SS_0_RX_MASK);
mask = sys_read32(SCSS_REGISTER_BASE + I2C_SS_0_STOP_MASK);
mask &= INT_ENABLE_ARC;
sys_write32(mask, SCSS_REGISTER_BASE + I2C_SS_0_STOP_MASK);
/* Connect the IRQs to ISR */
IRQ_CONNECT(I2C_SS_0_ERR_VECTOR, 1, i2c_qmsi_ss_isr,
DEVICE_GET(i2c_ss_0), 0);
IRQ_CONNECT(I2C_SS_0_RX_VECTOR, 1, i2c_qmsi_ss_isr,
DEVICE_GET(i2c_ss_0), 0);
IRQ_CONNECT(I2C_SS_0_TX_VECTOR, 1, i2c_qmsi_ss_isr,
DEVICE_GET(i2c_ss_0), 0);
IRQ_CONNECT(I2C_SS_0_STOP_VECTOR, 1, i2c_qmsi_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_0 */
#ifdef CONFIG_I2C_1
static struct i2c_qmsi_ss_driver_data driver_data_1;
static void i2c_qmsi_ss_config_irq_1(void);
static struct i2c_qmsi_ss_config_info config_info_1 = {
.instance = QM_SS_I2C_1,
.default_cfg.raw = CONFIG_I2C_1_DEFAULT_CFG,
.irq_cfg = i2c_qmsi_ss_config_irq_1,
};
DEVICE_INIT(i2c_ss_1, CONFIG_I2C_1_NAME, i2c_qmsi_ss_init, &driver_data_1,
&config_info_1, SECONDARY, CONFIG_KERNEL_INIT_PRIORITY_DEVICE);
static void i2c_qmsi_ss_config_irq_1(void)
{
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 = sys_read32(SCSS_REGISTER_BASE + I2C_SS_1_ERR_MASK);
mask &= INT_ENABLE_ARC;
sys_write32(mask, SCSS_REGISTER_BASE + I2C_SS_1_ERR_MASK);
mask = sys_read32(SCSS_REGISTER_BASE + I2C_SS_1_TX_MASK);
mask &= INT_ENABLE_ARC;
sys_write32(mask, SCSS_REGISTER_BASE + I2C_SS_1_TX_MASK);
mask = sys_read32(SCSS_REGISTER_BASE + I2C_SS_1_RX_MASK);
mask &= INT_ENABLE_ARC;
sys_write32(mask, SCSS_REGISTER_BASE + I2C_SS_1_RX_MASK);
mask = sys_read32(SCSS_REGISTER_BASE + I2C_SS_1_STOP_MASK);
mask &= INT_ENABLE_ARC;
sys_write32(mask, SCSS_REGISTER_BASE + I2C_SS_1_STOP_MASK);
/* Connect the IRQs to ISR */
IRQ_CONNECT(I2C_SS_1_ERR_VECTOR, 1, i2c_qmsi_ss_isr,
DEVICE_GET(i2c_ss_1), 0);
IRQ_CONNECT(I2C_SS_1_RX_VECTOR, 1, i2c_qmsi_ss_isr,
DEVICE_GET(i2c_ss_1), 0);
IRQ_CONNECT(I2C_SS_1_TX_VECTOR, 1, i2c_qmsi_ss_isr,
DEVICE_GET(i2c_ss_1), 0);
IRQ_CONNECT(I2C_SS_1_STOP_VECTOR, 1, i2c_qmsi_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_1 */
static int i2c_qmsi_ss_configure(struct device *dev, uint32_t config)
{
qm_ss_i2c_t instance = GET_CONTROLLER_INSTANCE(dev);
union dev_config cfg;
qm_ss_i2c_config_t qm_cfg;
cfg.raw = config;
/* This driver only supports master mode. */
if (!cfg.bits.is_master_device) {
return -EINVAL;
}
qm_cfg.address_mode = (cfg.bits.use_10_bit_addr) ? QM_SS_I2C_10_BIT :
QM_SS_I2C_7_BIT;
switch (cfg.bits.speed) {
case I2C_SPEED_STANDARD:
qm_cfg.speed = QM_SS_I2C_SPEED_STD;
break;
case I2C_SPEED_FAST:
qm_cfg.speed = QM_SS_I2C_SPEED_FAST;
break;
default:
return -EINVAL;
}
if (qm_ss_i2c_set_config(instance, &qm_cfg) != 0) {
return -EIO;
}
return 0;
}
static void transfer_complete(void *data, int rc, qm_ss_i2c_status_t status,
uint32_t len)
{
struct device *dev = data;
struct i2c_qmsi_ss_driver_data *driver_data;
driver_data = GET_DRIVER_DATA(dev);
driver_data->transfer_status = rc;
device_sync_call_complete(&driver_data->sync);
}
static int i2c_qmsi_ss_transfer(struct device *dev, struct i2c_msg *msgs,
uint8_t num_msgs, uint16_t addr)
{
struct i2c_qmsi_ss_driver_data *driver_data = GET_DRIVER_DATA(dev);
qm_ss_i2c_t instance = GET_CONTROLLER_INSTANCE(dev);
qm_ss_i2c_status_t status;
int rc;
qm_ss_i2c_get_status(instance, &status);
if (status != QM_I2C_IDLE) {
return -EBUSY;
}
if (msgs == NULL || num_msgs == 0) {
return -ENOTSUP;
}
for (int i = 0; i < num_msgs; i++) {
uint8_t *buf = msgs[i].buf;
uint32_t len = msgs[i].len;
uint8_t op = msgs[i].flags & I2C_MSG_RW_MASK;
bool stop = (msgs[i].flags & I2C_MSG_STOP) == I2C_MSG_STOP;
qm_ss_i2c_transfer_t xfer = { 0 };
if (op == I2C_MSG_WRITE) {
xfer.tx = buf;
xfer.tx_len = len;
} else {
xfer.rx = buf;
xfer.rx_len = len;
}
xfer.callback = transfer_complete;
xfer.callback_data = dev;
xfer.stop = stop;
rc = qm_ss_i2c_master_irq_transfer(instance, &xfer, addr);
if (rc != 0)
return -EIO;
/* Block current thread until the I2C transfer completes. */
if (stop || op != I2C_MSG_WRITE) {
device_sync_call_wait(&driver_data->sync);
if (driver_data->transfer_status != 0)
return -EIO;
}
}
return 0;
}
static int i2c_qmsi_ss_suspend(struct device *dev)
{
ARG_UNUSED(dev);
return -ENODEV;
}
static int i2c_qmsi_ss_resume(struct device *dev)
{
ARG_UNUSED(dev);
return -ENODEV;
}
static struct i2c_driver_api api = {
.configure = i2c_qmsi_ss_configure,
.transfer = i2c_qmsi_ss_transfer,
.suspend = i2c_qmsi_ss_suspend,
.resume = i2c_qmsi_ss_resume,
};
static int i2c_qmsi_ss_init(struct device *dev)
{
struct i2c_qmsi_ss_driver_data *driver_data = GET_DRIVER_DATA(dev);
struct i2c_qmsi_ss_config_info *config = dev->config->config_info;
qm_ss_i2c_t instance = GET_CONTROLLER_INSTANCE(dev);
int err;
config->irq_cfg();
ss_clk_i2c_enable(instance);
err = i2c_qmsi_ss_configure(dev, config->default_cfg.raw);
if (err < 0) {
return err;
}
device_sync_call_init(&driver_data->sync);
dev->driver_api = &api;
return 0;
}

View file

@ -17,3 +17,4 @@ obj-$(CONFIG_SOC_FLASH_QMSI) += drivers/qm_flash.o
obj-$(CONFIG_PINMUX_DEV_QMSI) += drivers/qm_pinmux.o obj-$(CONFIG_PINMUX_DEV_QMSI) += drivers/qm_pinmux.o
obj-$(CONFIG_SPI_QMSI_SS) += drivers/sensor/qm_ss_spi.o obj-$(CONFIG_SPI_QMSI_SS) += drivers/sensor/qm_ss_spi.o
obj-$(CONFIG_GPIO_QMSI_SS) += drivers/sensor/qm_ss_gpio.o obj-$(CONFIG_GPIO_QMSI_SS) += drivers/sensor/qm_ss_gpio.o
obj-$(CONFIG_I2C_QMSI_SS) += drivers/sensor/qm_ss_i2c.o