i2c: Enable interrupts for QMSI shim driver

Due to an issue with the interrupt-based transfer API from QMSI driver,
the 'transfer' callback from the shim driver (i2c_qmsi_transfer) is
implemented with polling APIs. This is not ideal because we are not able
to sleep the current thread (so another task can be scheduled in) while
the i2c operation is carried out.

The interrupt issue with the QMSI driver has been solved then this patch
fixes the shim driver so it uses the interrupt-based API and adds extra
code to handle the thread synchronization.

Finally, this patch also moves all 'struct device' related definitions
from the bottom to the top of the i2c_qmsi.c file so the DEVICE_GET
macro can be used in transfer_complete() and removes the init.h include
since it is not needed anymore.

Change-Id: I7ef7ce4cea6fcc939e310e5fe12c406645f6a16e
Signed-off-by: Andre Guedes <andre.guedes@intel.com>
This commit is contained in:
Andre Guedes 2016-01-28 15:16:53 -02:00 committed by Anas Nashif
commit dacf54de68
3 changed files with 142 additions and 35 deletions

View file

@ -152,6 +152,20 @@ config I2C_DW_1_IRQ
default 1 default 1
endif # I2C_DW endif # I2C_DW
if I2C_QMSI
config I2C_QMSI_0_IRQ
default 0
config I2C_QMSI_0_INT_PRIORITY
default 2
config I2C_QMSI_1_IRQ
default 1
config I2C_QMSI_1_INT_PRIORITY
default 2
endif # I2C_QMSI
endif # I2C endif # I2C
if CLOCK_CONTROL if CLOCK_CONTROL

View file

@ -315,6 +315,14 @@ config I2C_QMSI_0_NAME
depends on I2C_QMSI_0 depends on I2C_QMSI_0
default "I2C0" default "I2C0"
config I2C_QMSI_0_IRQ
int "IRQ number from I2C_0 controller"
depends on I2C_QMSI_0
config I2C_QMSI_0_INT_PRIORITY
int "Interrupt priority from I2C_0 controller"
depends on I2C_QMSI_0
config I2C_QMSI_1 config I2C_QMSI_1
bool "Enable I2C_1 controller" bool "Enable I2C_1 controller"
default n default n
@ -324,3 +332,11 @@ config I2C_QMSI_1_NAME
string "I2C_1 device name" string "I2C_1 device name"
depends on I2C_QMSI_1 depends on I2C_QMSI_1
default "I2C1" default "I2C1"
config I2C_QMSI_1_IRQ
int "IRQ number from I2C_1 controller"
depends on I2C_QMSI_1
config I2C_QMSI_1_INT_PRIORITY
int "Interrupt priority from I2C_1 controller"
depends on I2C_QMSI_1

View file

@ -16,21 +16,54 @@
#include <device.h> #include <device.h>
#include <i2c.h> #include <i2c.h>
#include <init.h> #include <ioapic.h>
#include "qm_i2c.h" #include "qm_i2c.h"
#include "qm_scss.h" #include "qm_scss.h"
/* Convenient macro to get the controller instance from the 'config_info' /* Convenient macros to get the controller instance and the driver data. */
* field defined in the 'struct device_config'.
*/
#define GET_CONTROLLER_INSTANCE(dev) \ #define GET_CONTROLLER_INSTANCE(dev) \
(((struct i2c_qmsi_config_info *)dev->config->config_info)->instance) (((struct i2c_qmsi_config_info *)dev->config->config_info)->instance)
#define GET_DRIVER_DATA(dev) \
((struct i2c_qmsi_driver_data *)dev->driver_data)
struct i2c_qmsi_config_info { struct i2c_qmsi_config_info {
qm_i2c_t instance; /* Controller instance. */ qm_i2c_t instance; /* Controller instance. */
}; };
struct i2c_qmsi_driver_data {
device_sync_call_t sync;
qm_rc_t transfer_status;
};
static int i2c_qmsi_init(struct device *dev);
#ifdef CONFIG_I2C_QMSI_0
static struct i2c_qmsi_driver_data driver_data_0;
static struct i2c_qmsi_config_info config_info_0 = {
.instance = QM_I2C_0,
};
DEVICE_INIT(i2c_0, CONFIG_I2C_QMSI_0_NAME, i2c_qmsi_init, &driver_data_0,
&config_info_0, SECONDARY, CONFIG_KERNEL_INIT_PRIORITY_DEVICE);
#endif /* CONFIG_I2C_QMSI_0 */
#ifdef CONFIG_I2C_QMSI_1
static struct i2c_qmsi_driver_data driver_data_1;
static struct i2c_qmsi_config_info config_info_1 = {
.instance = QM_I2C_1,
};
DEVICE_INIT(i2c_1, CONFIG_I2C_QMSI_1_NAME, i2c_qmsi_init, &driver_data_1,
&config_info_1, SECONDARY, CONFIG_KERNEL_INIT_PRIORITY_DEVICE);
#endif /* CONFIG_I2C_QMSI_1 */
static int i2c_qmsi_configure(struct device *dev, uint32_t config) static int i2c_qmsi_configure(struct device *dev, uint32_t config)
{ {
qm_i2c_t instance = GET_CONTROLLER_INSTANCE(dev); qm_i2c_t instance = GET_CONTROLLER_INSTANCE(dev);
@ -67,15 +100,45 @@ static int i2c_qmsi_configure(struct device *dev, uint32_t config)
return DEV_OK; return DEV_OK;
} }
/* FIXME: This function is implemented using QMSI polling APIs since static void transfer_complete(uint32_t id, qm_rc_t status)
* the interrupt API (qm_i2c_master_irq_transfer) seems not to be {
* working properly. Once this issue is solved, we should change this struct device *dev;
* function to use the interrupt API and sleep the current thread while struct i2c_qmsi_driver_data *driver_data;
* the I2C transfer is carried out.
*/ switch (id) {
#ifdef CONFIG_I2C_QMSI_0
case QM_I2C_0:
dev = DEVICE_GET(i2c_0);
break;
#endif
#ifdef CONFIG_I2C_QMSI_1
case QM_I2C_1:
dev = DEVICE_GET(i2c_1);
break;
#endif
default:
return;
}
driver_data = GET_DRIVER_DATA(dev);
driver_data->transfer_status = status;
device_sync_call_complete(&driver_data->sync);
}
static void complete_cb(uint32_t id, uint32_t len)
{
transfer_complete(id, QM_RC_OK);
}
static void err_cb(uint32_t id, qm_i2c_status_t status)
{
transfer_complete(id, status);
}
static int i2c_qmsi_transfer(struct device *dev, struct i2c_msg *msgs, static int i2c_qmsi_transfer(struct device *dev, struct i2c_msg *msgs,
uint8_t num_msgs, uint16_t addr) uint8_t num_msgs, uint16_t addr)
{ {
struct i2c_qmsi_driver_data *driver_data = GET_DRIVER_DATA(dev);
qm_i2c_t instance = GET_CONTROLLER_INSTANCE(dev); qm_i2c_t instance = GET_CONTROLLER_INSTANCE(dev);
qm_rc_t rc; qm_rc_t rc;
@ -90,13 +153,31 @@ static int i2c_qmsi_transfer(struct device *dev, struct i2c_msg *msgs,
uint32_t len = msgs[i].len; uint32_t len = msgs[i].len;
uint8_t op = msgs[i].flags & I2C_MSG_RW_MASK; uint8_t op = msgs[i].flags & I2C_MSG_RW_MASK;
bool stop = (msgs[i].flags & I2C_MSG_STOP) == I2C_MSG_STOP; bool stop = (msgs[i].flags & I2C_MSG_STOP) == I2C_MSG_STOP;
qm_i2c_transfer_t xfer = { 0 };
rc = (op == I2C_MSG_WRITE) ? if (op == I2C_MSG_WRITE) {
qm_i2c_master_write(instance, addr, buf, len, stop) : xfer.tx = buf;
qm_i2c_master_read(instance, addr, buf, len, stop); xfer.tx_len = len;
xfer.tx_callback = complete_cb;
} else {
xfer.rx = buf;
xfer.rx_len = len;
xfer.rx_callback = complete_cb;
}
xfer.id = instance;
xfer.stop = stop;
xfer.err_callback = err_cb;
rc = qm_i2c_master_irq_transfer(instance, &xfer, addr);
if (rc != QM_RC_OK) if (rc != QM_RC_OK)
return DEV_FAIL; return DEV_FAIL;
/* Block current thread until the I2C transfer completes. */
device_sync_call_wait(&driver_data->sync);
if (driver_data->transfer_status != QM_RC_OK)
return DEV_FAIL;
} }
return DEV_OK; return DEV_OK;
@ -121,41 +202,37 @@ static struct i2c_driver_api api = {
static int i2c_qmsi_init(struct device *dev) static int i2c_qmsi_init(struct device *dev)
{ {
struct i2c_qmsi_driver_data *driver_data = GET_DRIVER_DATA(dev);
qm_i2c_t instance = GET_CONTROLLER_INSTANCE(dev); qm_i2c_t instance = GET_CONTROLLER_INSTANCE(dev);
switch (instance) { switch (instance) {
case QM_I2C_0: case QM_I2C_0:
/* Register interrupt handler, unmask IRQ and route it
* to Lakemont core.
*/
IRQ_CONNECT(CONFIG_I2C_QMSI_0_IRQ,
CONFIG_I2C_QMSI_0_INT_PRIORITY, qm_i2c_0_isr, NULL,
(IOAPIC_LEVEL | IOAPIC_HIGH));
irq_enable(CONFIG_I2C_QMSI_0_IRQ);
QM_SCSS_INT->int_i2c_mst_0_mask &= ~BIT(0);
clk_periph_enable(CLK_PERIPH_I2C_M0_REGISTER); clk_periph_enable(CLK_PERIPH_I2C_M0_REGISTER);
break; break;
case QM_I2C_1: case QM_I2C_1:
IRQ_CONNECT(CONFIG_I2C_QMSI_1_IRQ,
CONFIG_I2C_QMSI_1_INT_PRIORITY, qm_i2c_1_isr, NULL,
(IOAPIC_LEVEL | IOAPIC_HIGH));
irq_enable(CONFIG_I2C_QMSI_1_IRQ);
QM_SCSS_INT->int_i2c_mst_1_mask &= ~BIT(0);
clk_periph_enable(CLK_PERIPH_I2C_M1_REGISTER); clk_periph_enable(CLK_PERIPH_I2C_M1_REGISTER);
break; break;
default: default:
return DEV_FAIL; return DEV_FAIL;
} }
device_sync_call_init(&driver_data->sync);
dev->driver_api = &api; dev->driver_api = &api;
return DEV_OK; return DEV_OK;
} }
#ifdef CONFIG_I2C_QMSI_0
static struct i2c_qmsi_config_info config_info_0 = {
.instance = QM_I2C_0,
};
DEVICE_INIT(i2c_0, CONFIG_I2C_QMSI_0_NAME, i2c_qmsi_init, 0, &config_info_0,
SECONDARY, CONFIG_KERNEL_INIT_PRIORITY_DEVICE);
#endif /* CONFIG_I2C_QMSI_0 */
#ifdef CONFIG_I2C_QMSI_1
static struct i2c_qmsi_config_info config_info_1 = {
.instance = QM_I2C_1,
};
DEVICE_INIT(i2c_1, CONFIG_I2C_QMSI_1_NAME, i2c_qmsi_init, 0, &config_info_1,
SECONDARY, CONFIG_KERNEL_INIT_PRIORITY_DEVICE);
#endif /* CONFIG_I2C_QMSI_1 */