191 lines
6.1 KiB
C
191 lines
6.1 KiB
C
/*
|
|
* Copyright (c) 2016, 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"
|
|
|
|
/* Register offsets from a base register for a particular mailbox channel. */
|
|
#define QM_MBOX_CTRL_OFFSET (0x00)
|
|
#define QM_MBOX_DATA0_OFFSET (0x04)
|
|
#define QM_MBOX_DATA1_OFFSET (0x08)
|
|
#define QM_MBOX_DATA2_OFFSET (0x0C)
|
|
#define QM_MBOX_DATA3_OFFSET (0x10)
|
|
#define QM_MBOX_STATUS_OFFSET (0x14)
|
|
|
|
#define QM_MBOX_SS_MASK_OFFSET (0x8)
|
|
|
|
/* Private data structure maintained by the driver */
|
|
typedef struct qm_mailbox_info_t {
|
|
/*!< Callback function registered with the application. */
|
|
qm_mbox_callback_t mpr_cb;
|
|
/*!< Callback function data return via the callback function. */
|
|
void *cb_data;
|
|
} qm_mailbox_info_t;
|
|
|
|
/* Mailbox channels private data structures */
|
|
static qm_mailbox_info_t mailbox_devs[QM_MBOX_CH_NUM];
|
|
|
|
QM_ISR_DECLARE(qm_mbox_isr)
|
|
{
|
|
qm_mailbox_t *const mbox_reg = (qm_mailbox_t *)QM_SCSS_MAILBOX;
|
|
uint8_t i = 0;
|
|
uint8_t mask;
|
|
uint16_t chall_sts = QM_SCSS_MAILBOX->mbox_chall_sts;
|
|
|
|
/*
|
|
* Interrupt masking register is has a bit assigned per MBOX channel.
|
|
* QM_SCSS_INT 0-7 bits are MBOX interrupt gates to APIC, 8-15 are
|
|
* gating interrupts to Sensors PIC.
|
|
*/
|
|
#if (QM_SENSOR)
|
|
mask = 0xff & (QM_SCSS_INT->int_mailbox_mask >> QM_MBOX_SS_MASK_OFFSET);
|
|
#else
|
|
mask = 0xff & QM_SCSS_INT->int_mailbox_mask;
|
|
#endif
|
|
|
|
for (i = 0; chall_sts; i++, chall_sts >>= 2) {
|
|
if ((chall_sts & QM_MBOX_CH_INT) == 0) {
|
|
continue;
|
|
}
|
|
if (mask & BIT(i)) {
|
|
continue;
|
|
}
|
|
if (mbox_reg[i].ch_sts & QM_MBOX_CH_INT) {
|
|
if (NULL != mailbox_devs[i].mpr_cb) {
|
|
/* Callback */
|
|
mailbox_devs[i].mpr_cb(mailbox_devs[i].cb_data);
|
|
}
|
|
/* Clear the interrupt */
|
|
mbox_reg[i].ch_sts = QM_MBOX_CH_INT;
|
|
}
|
|
}
|
|
|
|
QM_ISR_EOI(QM_IRQ_MBOX_VECTOR);
|
|
}
|
|
|
|
int qm_mbox_ch_set_config(const qm_mbox_ch_t mbox_ch, qm_mbox_callback_t mpr_cb,
|
|
void *cb_data, const bool irq_en)
|
|
{
|
|
uint32_t mask;
|
|
|
|
QM_CHECK(mbox_ch < QM_MBOX_CH_NUM, -EINVAL);
|
|
|
|
/* Block interrupts while configuring MBOX */
|
|
qm_irq_mask(QM_IRQ_MBOX);
|
|
|
|
#if (QM_SENSOR)
|
|
/* MBOX Interrupt Routing gate to SS core. */
|
|
mask = BIT((mbox_ch + QM_MBOX_SS_MASK_OFFSET));
|
|
#else
|
|
/* MBOX Interrupt Routing gate to LMT core. */
|
|
mask = BIT(mbox_ch);
|
|
#endif
|
|
|
|
/* Register callback function */
|
|
mailbox_devs[mbox_ch].mpr_cb = mpr_cb;
|
|
mailbox_devs[mbox_ch].cb_data = cb_data;
|
|
|
|
if (irq_en == true) {
|
|
/* Note: Routing is done now, cannot be done in irq_request! */
|
|
QM_SCSS_INT->int_mailbox_mask &= ~mask;
|
|
/* Clear the interrupt */
|
|
((qm_mailbox_t *)QM_SCSS_MAILBOX + mbox_ch)->ch_sts =
|
|
QM_MBOX_CH_INT;
|
|
} else {
|
|
/* Note: Routing is done now, cannot be done in irq_request! */
|
|
QM_SCSS_INT->int_mailbox_mask |= mask;
|
|
}
|
|
|
|
/* UnBlock MBOX interrupts. */
|
|
qm_irq_unmask(QM_IRQ_MBOX);
|
|
return 0;
|
|
}
|
|
|
|
int qm_mbox_ch_write(const qm_mbox_ch_t mbox_ch,
|
|
const qm_mbox_msg_t *const data)
|
|
{
|
|
qm_mailbox_t *const mbox_reg = (qm_mailbox_t *)QM_SCSS_MAILBOX +
|
|
mbox_ch;
|
|
|
|
/* Check if the previous message has been consumed. */
|
|
if (!(mbox_reg->ch_ctrl & QM_MBOX_TRIGGER_CH_INT)) {
|
|
/* Write the payload data to the mailbox channel. */
|
|
mbox_reg->ch_data[0] = data->data[QM_MBOX_PAYLOAD_0];
|
|
mbox_reg->ch_data[1] = data->data[QM_MBOX_PAYLOAD_1];
|
|
mbox_reg->ch_data[2] = data->data[QM_MBOX_PAYLOAD_2];
|
|
mbox_reg->ch_data[3] = data->data[QM_MBOX_PAYLOAD_3];
|
|
/* Write the control word and trigger the channel interrupt. */
|
|
mbox_reg->ch_ctrl = data->ctrl | QM_MBOX_TRIGGER_CH_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 data)
|
|
{
|
|
qm_mailbox_t *const mbox_reg = (qm_mailbox_t *)QM_SCSS_MAILBOX + mbox_ch;
|
|
|
|
/* Read data from the mailbox channel and clear bit 31 of the
|
|
* control word. */
|
|
data->ctrl = mbox_reg->ch_ctrl & ~QM_MBOX_TRIGGER_CH_INT;
|
|
data->data[QM_MBOX_PAYLOAD_0] = mbox_reg->ch_data[0];
|
|
data->data[QM_MBOX_PAYLOAD_1] = mbox_reg->ch_data[1];
|
|
data->data[QM_MBOX_PAYLOAD_2] = mbox_reg->ch_data[2];
|
|
data->data[QM_MBOX_PAYLOAD_3] = mbox_reg->ch_data[3];
|
|
|
|
/* Check if the message has arrived. */
|
|
if (mbox_reg->ch_sts & QM_MBOX_CH_DATA) {
|
|
/* Clear data status bit */
|
|
mbox_reg->ch_sts = QM_MBOX_CH_DATA;
|
|
return 0;
|
|
}
|
|
/* there is no new data in mailbox */
|
|
return -EIO;
|
|
}
|
|
|
|
int qm_mbox_ch_get_status(const qm_mbox_ch_t mbox_ch,
|
|
qm_mbox_ch_status_t *const status)
|
|
{
|
|
QM_CHECK(mbox_ch < QM_MBOX_CH_NUM, -EINVAL);
|
|
qm_mailbox_t *const mbox_reg = (qm_mailbox_t *)QM_SCSS_MAILBOX +
|
|
mbox_ch;
|
|
*status = mbox_reg->ch_sts &QM_MBOX_CH_STATUS_MASK;
|
|
return 0;
|
|
}
|
|
|
|
int qm_mbox_ch_data_ack(const qm_mbox_ch_t mbox_ch)
|
|
{
|
|
QM_CHECK(mbox_ch < QM_MBOX_CH_NUM, -EINVAL);
|
|
((qm_mailbox_t *)QM_SCSS_MAILBOX + mbox_ch)->ch_sts = QM_MBOX_CH_DATA;
|
|
return 0;
|
|
}
|