| /* |
| * 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; |
| } |