IPI: Implement Quark SE mailbox interface
Implements a low-level IPI driver for Quark SE mailboxes. Configures the Quark SE platform to initialize it and configure an IPI console from ARC->LMT on channel 4. Change-Id: I30123771d04c2e06ea6fcca585fd4ef74c0717a7 Signed-off-by: Anas Nashif <anas.nashif@intel.com> Signed-off-by: Andrew Boie <andrew.p.boie@intel.com> Signed-off-by: Anas Nashif <anas.nashif@intel.com>
This commit is contained in:
parent
6406dc6a66
commit
3937f84c9f
7 changed files with 439 additions and 0 deletions
|
@ -59,4 +59,6 @@ source "drivers/watchdog/Kconfig"
|
|||
|
||||
source "drivers/clock_control/Kconfig"
|
||||
|
||||
source "drivers/ipi/Kconfig"
|
||||
|
||||
endmenu
|
||||
|
|
|
@ -21,3 +21,4 @@ obj-$(CONFIG_ETHERNET) += ethernet/
|
|||
obj-$(CONFIG_WATCHDOG) += watchdog/
|
||||
obj-$(CONFIG_RTC) += rtc/
|
||||
obj-$(CONFIG_CLOCK_CONTROL) += clock_control/
|
||||
obj-$(CONFIG_IPI) += ipi/
|
||||
|
|
24
drivers/ipi/Kconfig
Normal file
24
drivers/ipi/Kconfig
Normal file
|
@ -0,0 +1,24 @@
|
|||
menuconfig IPI
|
||||
bool
|
||||
prompt "IPI drivers"
|
||||
default n
|
||||
help
|
||||
Include interrupt-based inter-processor communication
|
||||
drivers in system configuration
|
||||
|
||||
config IPI_QUARK_SE
|
||||
bool "Quark SE Mailbox IPI driver"
|
||||
default n
|
||||
depends on IPI
|
||||
help
|
||||
IPI driver for Quark SE mailboxes
|
||||
|
||||
config IPI_QUARK_SE_MASTER
|
||||
bool "Quark SE Mailbox IPI master controller"
|
||||
default n
|
||||
depends on IPI_QUARK_SE
|
||||
help
|
||||
Enable this for the first CPU that initializes IPI.
|
||||
Sets up the initial interrupt mask and clears out all
|
||||
channels. Should be turned on for one CPU only.
|
||||
|
4
drivers/ipi/Makefile
Normal file
4
drivers/ipi/Makefile
Normal file
|
@ -0,0 +1,4 @@
|
|||
ccflags-y += -I$(srctree/drivers)
|
||||
|
||||
obj-$(CONFIG_IPI_QUARK_SE) += ipi_quark_se.o ipi_static_irq_stubs.o
|
||||
|
239
drivers/ipi/ipi_quark_se.c
Normal file
239
drivers/ipi/ipi_quark_se.c
Normal file
|
@ -0,0 +1,239 @@
|
|||
/* ipi_quark_se.c - Quark SE mailbox driver */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2015 Intel Corporation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <nanokernel.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <device.h>
|
||||
#include <init.h>
|
||||
#include <ipi.h>
|
||||
#include <arch/cpu.h>
|
||||
#include <misc/printk.h>
|
||||
#include <misc/__assert.h>
|
||||
#include <errno.h>
|
||||
#include "ipi_quark_se.h"
|
||||
|
||||
|
||||
/* We have a single ISR for all channels, so in order to properly handle
|
||||
* messages we need to figure out which device object corresponds to
|
||||
* in incoming channel */
|
||||
static struct device *device_by_channel[QUARK_SE_IPI_CHANNELS];
|
||||
static uint32_t inbound_channels;
|
||||
|
||||
static uint32_t quark_se_ipi_sts_get(void)
|
||||
{
|
||||
return sys_read32(QUARK_SE_IPI_CHALL_STS) & inbound_channels;
|
||||
}
|
||||
|
||||
static void mailbox_handle(struct device *d)
|
||||
{
|
||||
struct quark_se_ipi_config_info *config;
|
||||
struct quark_se_ipi_driver_data *driver_data;
|
||||
volatile struct quark_se_ipi *ipi;
|
||||
|
||||
config = d->config->config_info;
|
||||
driver_data = d->driver_data;
|
||||
ipi = config->ipi;
|
||||
|
||||
if (driver_data->callback) {
|
||||
driver_data->callback(driver_data->callback_ctx,
|
||||
ipi->ctrl.ctrl, &ipi->data);
|
||||
}
|
||||
|
||||
ipi->sts.irq = 1; /* Clear the interrupt bit */
|
||||
ipi->sts.sts = 1; /* Clear channel status bit */
|
||||
}
|
||||
|
||||
|
||||
static void set_channel_irq_state(int channel, int enable)
|
||||
{
|
||||
mem_addr_t addr = QUARK_SE_IPI_MASK;
|
||||
int bit = channel + QUARK_SE_IPI_MASK_START_BIT;
|
||||
|
||||
if (enable) {
|
||||
sys_clear_bit(addr, bit);
|
||||
} else {
|
||||
sys_set_bit(addr, bit);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Interrupt handler, gets messages on all incoming enabled mailboxes */
|
||||
void quark_se_ipi_isr(void *param)
|
||||
{
|
||||
int channel;
|
||||
int sts;
|
||||
struct device *d;
|
||||
|
||||
ARG_UNUSED(param);
|
||||
|
||||
/* Find out which mailbox channel has an incoming message */
|
||||
while (1) {
|
||||
sts = quark_se_ipi_sts_get();
|
||||
|
||||
/* FIXME: for every message sent, there are two interrupts
|
||||
* generated, the second has empty sts. Probably an IRQ
|
||||
* triggering issue */
|
||||
if (!sts) {
|
||||
break;
|
||||
}
|
||||
|
||||
channel = (find_msb_set(sts) - 1) / 2;
|
||||
d = device_by_channel[channel];
|
||||
if (d) {
|
||||
mailbox_handle(d);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
static int quark_se_ipi_send(struct device *d, int wait, uint32_t id,
|
||||
const void *data, int size)
|
||||
{
|
||||
struct quark_se_ipi_config_info *config = d->config->config_info;
|
||||
volatile struct quark_se_ipi *ipi = config->ipi;
|
||||
const uint8_t *data8;
|
||||
int i;
|
||||
int flags;
|
||||
|
||||
if (id > QUARK_SE_IPI_MAX_ID_VAL) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (config->direction != QUARK_SE_IPI_OUTBOUND) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (size > QUARK_SE_IPI_DATA_BYTES) {
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
flags = irq_lock();
|
||||
|
||||
if (ipi->sts.sts != 0) {
|
||||
irq_unlock(flags);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
/* Populate the data, memcpy doesn't take volatiles */
|
||||
data8 = (const uint8_t *)data;
|
||||
|
||||
for (i = 0; i < size; ++i) {
|
||||
ipi->data[i] = data8[i];
|
||||
}
|
||||
ipi->ctrl.ctrl = id;
|
||||
|
||||
/* Cause the interrupt to assert on the remote side */
|
||||
ipi->ctrl.irq = 1;
|
||||
|
||||
/* Wait for HW to set the sts bit */
|
||||
while (ipi->sts.sts == 0) {
|
||||
}
|
||||
|
||||
if (wait) {
|
||||
/* Loop until remote clears the status bit */
|
||||
while (ipi->sts.sts != 0) {
|
||||
}
|
||||
}
|
||||
irq_unlock(flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int quark_se_ipi_max_data_size_get(struct device *d)
|
||||
{
|
||||
ARG_UNUSED(d);
|
||||
|
||||
return QUARK_SE_IPI_DATA_BYTES;
|
||||
}
|
||||
|
||||
|
||||
static uint32_t quark_se_ipi_max_id_val_get(struct device *d)
|
||||
{
|
||||
ARG_UNUSED(d);
|
||||
|
||||
return QUARK_SE_IPI_MAX_ID_VAL;
|
||||
}
|
||||
|
||||
static void quark_se_ipi_register_callback(struct device *d, ipi_callback_t cb,
|
||||
void *context)
|
||||
{
|
||||
struct quark_se_ipi_driver_data *driver_data = d->driver_data;
|
||||
|
||||
driver_data->callback = cb;
|
||||
driver_data->callback_ctx = context;
|
||||
}
|
||||
|
||||
|
||||
static int quark_se_ipi_set_enabled(struct device *d, int enable)
|
||||
{
|
||||
struct quark_se_ipi_config_info *config_info = d->config->config_info;
|
||||
|
||||
if (config_info->direction != QUARK_SE_IPI_INBOUND) {
|
||||
return -EINVAL;
|
||||
}
|
||||
set_channel_irq_state(config_info->channel, enable);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct ipi_driver_api api_funcs = {
|
||||
.send = quark_se_ipi_send,
|
||||
.register_callback = quark_se_ipi_register_callback,
|
||||
.max_data_size_get = quark_se_ipi_max_data_size_get,
|
||||
.max_id_val_get = quark_se_ipi_max_id_val_get,
|
||||
.set_enabled = quark_se_ipi_set_enabled
|
||||
};
|
||||
|
||||
int quark_se_ipi_controller_initialize(struct device *d)
|
||||
{
|
||||
struct quark_se_ipi_controller_config_info *config = d->config->config_info;
|
||||
#if CONFIG_IPI_QUARK_SE_MASTER
|
||||
int i;
|
||||
|
||||
/* Mask all mailbox interrupts, we'll enable them
|
||||
* individually later. Clear out any pending messages */
|
||||
sys_write32(0xFFFFFFFF, QUARK_SE_IPI_MASK);
|
||||
for (i = 0; i < QUARK_SE_IPI_CHANNELS; ++i) {
|
||||
volatile struct quark_se_ipi *ipi = QUARK_SE_IPI(i);
|
||||
ipi->sts.sts = 0;
|
||||
ipi->sts.irq = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (config->controller_init) {
|
||||
return config->controller_init();
|
||||
}
|
||||
return DEV_OK;
|
||||
}
|
||||
|
||||
|
||||
int quark_se_ipi_initialize(struct device *d)
|
||||
{
|
||||
struct quark_se_ipi_config_info *config = d->config->config_info;
|
||||
|
||||
device_by_channel[config->channel] = d;
|
||||
d->driver_api = &api_funcs;
|
||||
if (config->direction == QUARK_SE_IPI_INBOUND) {
|
||||
inbound_channels |= (0x3 << (config->channel * 2));
|
||||
}
|
||||
|
||||
return DEV_OK;
|
||||
}
|
||||
|
||||
|
141
drivers/ipi/ipi_quark_se.h
Normal file
141
drivers/ipi/ipi_quark_se.h
Normal file
|
@ -0,0 +1,141 @@
|
|||
/* ipi_quark_se.h - Quark SE mailbox driver */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2015 Intel Corporation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __INCquark_se_mailboxh
|
||||
#define __INCquark_se_mailboxh
|
||||
|
||||
#include <nanokernel.h>
|
||||
#include <board.h> /* for SCSS_REGISTER_BASE */
|
||||
#include <ipi.h>
|
||||
#include <device.h>
|
||||
#include <init.h>
|
||||
|
||||
#define QUARK_SE_IPI_OUTBOUND 0
|
||||
#define QUARK_SE_IPI_INBOUND 1
|
||||
|
||||
#if defined(CONFIG_PLATFORM_QUARK_SE_X86)
|
||||
/* First byte of the QUARK_SE_IPI_MASK register is for the Lakemont */
|
||||
#define QUARK_SE_IPI_MASK_START_BIT 0
|
||||
#define QUARK_SE_IPI_INTERRUPT 21
|
||||
#define QUARK_SE_IPI_ARC_LMT_DIR QUARK_SE_IPI_INBOUND
|
||||
#define QUARK_SE_IPI_LMT_ARC_DIR QUARK_SE_IPI_OUTBOUND
|
||||
|
||||
#elif defined(CONFIG_PLATFORM_QUARK_SE_ARC)
|
||||
/* Second byte is for ARC */
|
||||
#define QUARK_SE_IPI_MASK_START_BIT 8
|
||||
#define QUARK_SE_IPI_INTERRUPT 57
|
||||
#define QUARK_SE_IPI_ARC_LMT_DIR QUARK_SE_IPI_OUTBOUND
|
||||
#define QUARK_SE_IPI_LMT_ARC_DIR QUARK_SE_IPI_INBOUND
|
||||
|
||||
#else
|
||||
#error "Unsupported platform for ipi_quark_se driver"
|
||||
#endif
|
||||
|
||||
#define QUARK_SE_IPI_CHANNELS 8
|
||||
#define QUARK_SE_IPI_DATA_BYTES (4 * sizeof(uint32_t))
|
||||
#define QUARK_SE_IPI_MAX_ID_VAL 0x7FFFFFFF
|
||||
|
||||
/* QUARK_SE EAS section 28.5.1.123 */
|
||||
struct __packed quark_se_ipi_ch_ctrl {
|
||||
uint32_t ctrl : 31;
|
||||
uint32_t irq : 1;
|
||||
};
|
||||
|
||||
struct __packed quark_se_ipi_ch_sts {
|
||||
uint32_t sts : 1;
|
||||
uint32_t irq : 1;
|
||||
uint32_t reserved : 30;
|
||||
};
|
||||
|
||||
struct __packed quark_se_ipi {
|
||||
struct quark_se_ipi_ch_ctrl ctrl;
|
||||
uint8_t data[QUARK_SE_IPI_DATA_BYTES]; /* contiguous 32-bit registers */
|
||||
struct quark_se_ipi_ch_sts sts;
|
||||
};
|
||||
|
||||
/* Base address for mailboxes
|
||||
*
|
||||
* Layout:
|
||||
*
|
||||
* quark_se_ipi[8]
|
||||
* QUARK_SE_IPI_CHALL_STS
|
||||
*/
|
||||
#define QUARK_SE_IPI_BASE (SCSS_REGISTER_BASE + 0xa00)
|
||||
|
||||
/* 28.5.1.73 Host processor Interrupt routing mask 21
|
||||
*
|
||||
* Bits Description
|
||||
* 31:24 Mailbox SS Halt interrupt maskIUL
|
||||
* 23:16 Mailbox Host Halt interrupt mask
|
||||
* 15:8 Mailbox SS interrupt mask
|
||||
* 7:0 Mailbox Host interrupt mask
|
||||
*/
|
||||
#define QUARK_SE_IPI_MASK (SCSS_REGISTER_BASE + 0x4a0)
|
||||
|
||||
/* All status bits of the mailboxes
|
||||
*
|
||||
* Bits Description
|
||||
* 31:16 Reserved
|
||||
* 15:0 CHn_STS bits (sts/irq) for all channels
|
||||
*/
|
||||
#define QUARK_SE_IPI_CHALL_STS (SCSS_REGISTER_BASE + 0x0AC0)
|
||||
|
||||
#define QUARK_SE_IPI(channel) ((volatile struct quark_se_ipi *)(QUARK_SE_IPI_BASE + \
|
||||
((channel) * sizeof(struct quark_se_ipi))))
|
||||
|
||||
|
||||
/* XXX I pulled this number out of thin air -- how to choose
|
||||
* the right priority? */
|
||||
#define QUARK_SE_IPI_INTERRUPT_PRI 2
|
||||
|
||||
struct quark_se_ipi_controller_config_info {
|
||||
int (*controller_init)(void);
|
||||
};
|
||||
|
||||
struct quark_se_ipi_config_info {
|
||||
int channel;
|
||||
int direction;
|
||||
volatile struct quark_se_ipi *ipi;
|
||||
};
|
||||
|
||||
|
||||
struct quark_se_ipi_driver_data {
|
||||
ipi_callback_t callback;
|
||||
void *callback_ctx;
|
||||
};
|
||||
|
||||
void quark_se_ipi_isr(void *param);
|
||||
|
||||
int quark_se_ipi_initialize(struct device *d);
|
||||
int quark_se_ipi_controller_initialize(struct device *d);
|
||||
|
||||
#define QUARK_SE_IPI_DEFINE(name, ch, dir) \
|
||||
struct quark_se_ipi_config_info quark_se_ipi_config_##name = { \
|
||||
.ipi = QUARK_SE_IPI(ch), \
|
||||
.channel = ch, \
|
||||
.direction = dir \
|
||||
}; \
|
||||
struct quark_se_ipi_driver_data quark_se_ipi_runtime_##name; \
|
||||
DECLARE_DEVICE_INIT_CONFIG(name, _STRINGIFY(name), \
|
||||
quark_se_ipi_initialize, \
|
||||
&quark_se_ipi_config_##name); \
|
||||
pre_kernel_late_init(name, &quark_se_ipi_runtime_##name);
|
||||
|
||||
|
||||
#endif /* __INCquark_se_mailboxh */
|
28
drivers/ipi/ipi_static_irq_stubs.S
Normal file
28
drivers/ipi/ipi_static_irq_stubs.S
Normal file
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright (c) 2015, Intel Corporation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#define _ASMLANGUAGE
|
||||
|
||||
#ifdef CONFIG_X86_32
|
||||
#include <arch/x86/asm.h>
|
||||
#include <drivers/ioapic.h>
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_IPI_QUARK_SE)
|
||||
#if defined(CONFIG_IOAPIC)
|
||||
ioapic_mkstub quark_se_ipi quark_se_ipi_isr 0
|
||||
#endif
|
||||
#endif
|
Loading…
Add table
Add a link
Reference in a new issue