blob: 323076870a19f549e21f36d7161309f5cbcabb75 [file] [log] [blame]
/*
* 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;
}