284 lines
9.1 KiB
C
284 lines
9.1 KiB
C
/*
|
|
* Copyright (c) 2017, Intel Corporation
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright notice,
|
|
* this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
* this list of conditions and the following disclaimer in the documentation
|
|
* and/or other materials provided with the distribution.
|
|
* 3. Neither the name of the Intel Corporation nor the names of its
|
|
* contributors may be used to endorse or promote products derived from this
|
|
* software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE INTEL CORPORATION OR CONTRIBUTORS BE
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include "qm_common.h"
|
|
#include "qm_mailbox.h"
|
|
#include "qm_interrupt.h"
|
|
#include "qm_interrupt_router.h"
|
|
|
|
/**
|
|
* The Active core can be either Lakemont or the Sensor Sub-System.
|
|
* The active core depends on a compilation flag indicating which
|
|
* core is being used.
|
|
*
|
|
* Core specific mailbox #defines are grouped here to prevent
|
|
* duplication below.
|
|
*/
|
|
|
|
#if HAS_MAILBOX_LAKEMONT_DEST
|
|
#if QM_LAKEMONT
|
|
#define ACTIVE_CORE_DEST QM_MBOX_TO_LMT
|
|
#define MBOX_ACTIVE_CORE_ALL_INT_MASK QM_IR_MBOX_LMT_ALL_INT_MASK
|
|
#define MBOX_INT_LOCK_MASK(N) QM_IR_MBOX_LMT_INT_LOCK_MASK(N)
|
|
#define MBOX_INT_LOCK_HALT_MASK(N) QM_IR_MBOX_LMT_INT_LOCK_HALT_MASK(N)
|
|
#define MBOX_IS_INT_MASK_EN(N) QM_IR_MBOX_IS_LMT_INT_MASK_EN(N)
|
|
#define MBOX_ENABLE_INT_MASK(N) QM_IR_MBOX_ENABLE_LMT_INT_MASK(N)
|
|
#define MBOX_DISABLE_INT_MASK(N) QM_IR_MBOX_DISABLE_LMT_INT_MASK(N)
|
|
#endif /* QM_LAKEMONT */
|
|
#endif /* HAS_MAILBOX_LAKEMONT_DEST */
|
|
|
|
#if HAS_MAILBOX_SENSOR_SUB_SYSTEM_DEST
|
|
#if QM_SENSOR
|
|
#define ACTIVE_CORE_DEST QM_MBOX_TO_SS
|
|
#define MBOX_ACTIVE_CORE_ALL_INT_MASK QM_IR_MBOX_SS_ALL_INT_MASK
|
|
#define MBOX_INT_LOCK_MASK(N) QM_IR_MBOX_SS_INT_LOCK_HALT_MASK(N)
|
|
#define MBOX_INT_LOCK_HALT_MASK(N) QM_IR_MBOX_SS_INT_LOCK_MASK(N)
|
|
#define MBOX_IS_INT_MASK_EN(N) QM_IR_MBOX_IS_SS_INT_MASK_EN(N)
|
|
#define MBOX_ENABLE_INT_MASK(N) QM_IR_MBOX_ENABLE_SS_INT_MASK(N)
|
|
#define MBOX_DISABLE_INT_MASK(N) QM_IR_MBOX_DISABLE_SS_INT_MASK(N)
|
|
#endif /* QM_SENSOR */
|
|
#endif /* HAS_MAILBOX_SENSOR_SUB_SYSTEM_DEST */
|
|
|
|
#define MBOX_CHECK_DESTINATION(_dest) (ACTIVE_CORE_DEST == (_dest))
|
|
#define MBOX_CHECK_POLLING_MODE(_mode) (QM_MBOX_POLLING_MODE == (_mode))
|
|
|
|
static void mailbox_isr_handler(void);
|
|
|
|
/**
|
|
* Private data structure maintained by the driver
|
|
*/
|
|
typedef struct {
|
|
/** Destination of the mailbox channel. */
|
|
qm_mbox_destination_t dest;
|
|
/** Defines if the mailbox channel operates in interrupt
|
|
* mode or polling mode. */
|
|
qm_mbox_mode_t mode;
|
|
/** Callback function registered with the application. */
|
|
qm_mbox_callback_t callback;
|
|
/** Callback function data return via the callback function. */
|
|
void *callback_data;
|
|
} qm_mailbox_info_t;
|
|
|
|
/* Mailbox channels private data structures */
|
|
static qm_mailbox_info_t mailbox_devs[NUM_MAILBOXES];
|
|
|
|
QM_ISR_DECLARE(qm_mailbox_0_isr)
|
|
{
|
|
mailbox_isr_handler();
|
|
QM_ISR_EOI(QM_IRQ_MAILBOX_0_INT_VECTOR);
|
|
}
|
|
|
|
static void mailbox_isr_handler(void)
|
|
{
|
|
qm_mailbox_t *const mbox_reg = (qm_mailbox_t *)QM_MAILBOX;
|
|
uint8_t i = 0;
|
|
uint8_t mask;
|
|
uint16_t chall_sts = QM_MAILBOX->mbox_chall_sts;
|
|
|
|
mask = MBOX_ACTIVE_CORE_ALL_INT_MASK;
|
|
for (i = 0; chall_sts; i++, chall_sts >>= 2) {
|
|
if ((chall_sts & QM_MBOX_CH_STS_CTRL_INT) == 0) {
|
|
continue;
|
|
}
|
|
if (mask & BIT(i)) {
|
|
continue;
|
|
}
|
|
if (mbox_reg[i].ch_sts & QM_MBOX_CH_STS_CTRL_INT) {
|
|
if (NULL != mailbox_devs[i].callback) {
|
|
/* Callback */
|
|
mailbox_devs[i].callback(
|
|
mailbox_devs[i].callback_data);
|
|
}
|
|
/* Clear the interrupt */
|
|
mbox_reg[i].ch_sts = QM_MBOX_CH_STS_CTRL_INT;
|
|
}
|
|
}
|
|
}
|
|
|
|
int qm_mbox_ch_set_config(const qm_mbox_ch_t mbox_ch,
|
|
const qm_mbox_config_t *const config)
|
|
{
|
|
|
|
QM_CHECK((QM_MBOX_CH_0 <= mbox_ch) && (mbox_ch < NUM_MAILBOXES),
|
|
-EINVAL);
|
|
qm_mailbox_info_t *device = &mailbox_devs[mbox_ch];
|
|
|
|
/* Block interrupts while configuring MBOX */
|
|
QM_IR_MASK_INT(QM_IRQ_MAILBOX_0_INT);
|
|
|
|
/* Store the device destination */
|
|
device->dest = config->dest;
|
|
|
|
/* Check if we are enabling or disabling the channel. */
|
|
if (QM_MBOX_UNUSED != config->dest) {
|
|
|
|
if (QM_MBOX_INTERRUPT_MODE == config->mode) {
|
|
QM_CHECK(NULL != config->callback, -EINVAL);
|
|
|
|
/* Register callback function */
|
|
device->callback = config->callback;
|
|
/* Register callback function data */
|
|
device->callback_data = config->callback_data;
|
|
/* Update the mode of operation for the mailbox channel.
|
|
*/
|
|
device->mode = QM_MBOX_INTERRUPT_MODE;
|
|
|
|
/* Enable the mailbox interrupt if the lock is not set.
|
|
*/
|
|
if (!(MBOX_INT_LOCK_MASK(mbox_ch))) {
|
|
/* Note: Routing is done now, cannot be done in
|
|
* irq_request! */
|
|
MBOX_ENABLE_INT_MASK(mbox_ch);
|
|
} else {
|
|
/* The lock is set, but we need to check if the
|
|
* interrupt is routed */
|
|
QM_CHECK(MBOX_IS_INT_MASK_EN(mbox_ch), -EIO);
|
|
}
|
|
} else {
|
|
device->mode = QM_MBOX_POLLING_MODE;
|
|
/* Disable the mailbox interrupt if the lock is not set.
|
|
*/
|
|
if (!(MBOX_INT_LOCK_MASK(mbox_ch))) {
|
|
/* Note: Routing is done now, cannot be done in
|
|
* irq_request! */
|
|
MBOX_DISABLE_INT_MASK(mbox_ch);
|
|
}
|
|
|
|
device->callback = NULL;
|
|
device->callback_data = 0;
|
|
}
|
|
} else {
|
|
/* Disable the mailbox interrupt if the lock is not set. */
|
|
if (!(MBOX_INT_LOCK_MASK(mbox_ch))) {
|
|
/* Note: Routing is done now, cannot be done in
|
|
* irq_request! */
|
|
MBOX_DISABLE_INT_MASK(mbox_ch);
|
|
}
|
|
|
|
/* Set the mailbox channel to its default configuration. */
|
|
device->dest = QM_MBOX_UNUSED;
|
|
device->mode = QM_MBOX_INTERRUPT_MODE;
|
|
device->callback = NULL;
|
|
device->callback_data = 0;
|
|
}
|
|
|
|
/* UnBlock MBOX interrupts. */
|
|
QM_IR_UNMASK_INT(QM_IRQ_MAILBOX_0_INT);
|
|
return 0;
|
|
}
|
|
|
|
int qm_mbox_ch_write(const qm_mbox_ch_t mbox_ch, const qm_mbox_msg_t *const msg)
|
|
{
|
|
QM_CHECK((QM_MBOX_CH_0 <= mbox_ch) && (mbox_ch < NUM_MAILBOXES),
|
|
-EINVAL);
|
|
QM_CHECK(NULL != msg, -EINVAL);
|
|
qm_mailbox_t *const mbox_reg = (qm_mailbox_t *)QM_MAILBOX + mbox_ch;
|
|
|
|
uint32_t status = 0;
|
|
|
|
status = QM_MAILBOX->mbox[mbox_ch].ch_sts;
|
|
|
|
/* Check if the previous message has been consumed. */
|
|
if (false == (status & (QM_MBOX_CH_STS_CTRL_INT | QM_MBOX_CH_STS))) {
|
|
/* Write the payload data to the mailbox channel. */
|
|
mbox_reg->ch_data[0] = msg->data[QM_MBOX_PAYLOAD_0];
|
|
mbox_reg->ch_data[1] = msg->data[QM_MBOX_PAYLOAD_1];
|
|
mbox_reg->ch_data[2] = msg->data[QM_MBOX_PAYLOAD_2];
|
|
mbox_reg->ch_data[3] = msg->data[QM_MBOX_PAYLOAD_3];
|
|
/* Write the control word and trigger the channel interrupt. */
|
|
mbox_reg->ch_ctrl = msg->ctrl | QM_MBOX_CH_CTRL_INT;
|
|
return 0;
|
|
}
|
|
|
|
/* Previous message has not been consumed. */
|
|
return -EIO;
|
|
}
|
|
|
|
int qm_mbox_ch_read(const qm_mbox_ch_t mbox_ch, qm_mbox_msg_t *const msg)
|
|
{
|
|
QM_CHECK((QM_MBOX_CH_0 <= mbox_ch) && (mbox_ch < NUM_MAILBOXES),
|
|
-EINVAL);
|
|
QM_CHECK(NULL != msg, -EINVAL);
|
|
|
|
int rc = 0;
|
|
uint32_t status = 0;
|
|
|
|
qm_mailbox_t *mbox_reg = &QM_MAILBOX->mbox[mbox_ch];
|
|
|
|
if (MBOX_CHECK_DESTINATION(mailbox_devs[mbox_ch].dest)) {
|
|
status = mbox_reg->ch_sts;
|
|
|
|
/* If there is data pending consume it */
|
|
if (status & QM_MBOX_CH_STS) {
|
|
/* Read data from the mailbox channel and clear bit 31
|
|
* of the control word. */
|
|
msg->ctrl = mbox_reg->ch_ctrl & (~QM_MBOX_CH_CTRL_INT);
|
|
msg->data[0] = mbox_reg->ch_data[0];
|
|
msg->data[1] = mbox_reg->ch_data[1];
|
|
msg->data[2] = mbox_reg->ch_data[2];
|
|
msg->data[3] = mbox_reg->ch_data[3];
|
|
|
|
if (MBOX_CHECK_POLLING_MODE(
|
|
mailbox_devs[mbox_ch].mode)) {
|
|
/* In polling mode the interrupt status still
|
|
* needs to be cleared since we are not using
|
|
* the ISR. Note we write 1 to clear the bit.
|
|
*/
|
|
mbox_reg->ch_sts = QM_MBOX_CH_STS_CTRL_INT;
|
|
}
|
|
|
|
/* Clear data status bit. This indicates to others that
|
|
* the mailbox data has been consumed and a new message
|
|
* can be sent on the channel */
|
|
mbox_reg->ch_sts = QM_MBOX_CH_STS;
|
|
} else {
|
|
/* there is no pending data in the mailbox */
|
|
rc = -EIO;
|
|
}
|
|
} else {
|
|
/* Active destination has not been configured to consume data
|
|
* from this channel */
|
|
rc = -EINVAL;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
int qm_mbox_ch_get_status(const qm_mbox_ch_t mbox_ch,
|
|
qm_mbox_ch_status_t *const status)
|
|
{
|
|
QM_CHECK((QM_MBOX_CH_0 <= mbox_ch) && (mbox_ch < NUM_MAILBOXES),
|
|
-EINVAL);
|
|
QM_CHECK(NULL != status, -EINVAL);
|
|
|
|
*status = QM_MAILBOX->mbox[mbox_ch].ch_sts;
|
|
|
|
return 0;
|
|
}
|