/* ipm_quark_se.c - Quark SE mailbox driver */ /* * Copyright (c) 2015 Intel Corporation * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include #include #include #include #include #include #include "ipm_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_IPM_CHANNELS]; static u32_t inbound_channels; static u32_t quark_se_ipm_sts_get(void) { return sys_read32(QUARK_SE_IPM_CHALL_STS) & inbound_channels; } static void set_channel_irq_state(int channel, int enable) { mem_addr_t addr = QUARK_SE_IPM_MASK; int bit = channel + QUARK_SE_IPM_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_ipm_isr(void *param) { int channel; int sts, bit; struct device *d; const struct quark_se_ipm_config_info *config; struct quark_se_ipm_driver_data *driver_data; volatile struct quark_se_ipm *ipm; unsigned int key; ARG_UNUSED(param); while ((sts = quark_se_ipm_sts_get())) { __ASSERT(sts, "spurious IPM interrupt"); bit = find_msb_set(sts) - 1; channel = bit / 2; d = device_by_channel[channel]; __ASSERT(d, "got IRQ on channel with no IPM device"); config = d->config->config_info; driver_data = d->driver_data; ipm = config->ipm; __ASSERT(driver_data->callback, "enabled IPM channel with no callback"); driver_data->callback(driver_data->callback_ctx, ipm->ctrl & QUARK_SE_IPM_CTRL_CTRL_MASK, &ipm->data); key = irq_lock(); /* Clear the interrupt bit */ ipm->sts = QUARK_SE_IPM_STS_IRQ_BIT; /* Clear channel status bit */ ipm->sts = QUARK_SE_IPM_STS_STS_BIT; /* Wait for the above register writes to clear the channel * to propagate to the global channel status register */ while (quark_se_ipm_sts_get() & (0x3 << (channel * 2))) { /* Busy-wait */ } irq_unlock(key); } } static int quark_se_ipm_send(struct device *d, int wait, u32_t id, const void *data, int size) { const struct quark_se_ipm_config_info *config = d->config->config_info; volatile struct quark_se_ipm *ipm = config->ipm; u32_t data32[4]; /* Until we change API to u32_t array */ int flags; int i; if (id > QUARK_SE_IPM_MAX_ID_VAL) { return -EINVAL; } if (config->direction != QUARK_SE_IPM_OUTBOUND) { return -EINVAL; } if (size > QUARK_SE_IPM_DATA_REGS * sizeof(u32_t)) { return -EMSGSIZE; } flags = irq_lock(); if (ipm->sts & QUARK_SE_IPM_STS_STS_BIT) { irq_unlock(flags); return -EBUSY; } /* Actual message is passing using 32 bits registers */ memcpy(data32, data, size); for (i = 0; i < ARRAY_SIZE(data32); ++i) { ipm->data[i] = data32[i]; } ipm->ctrl = id | QUARK_SE_IPM_CTRL_IRQ_BIT; /* Wait for HW to set the sts bit */ while (!(ipm->sts & QUARK_SE_IPM_STS_STS_BIT)) { } irq_unlock(flags); if (wait) { /* Loop until remote clears the status bit */ while (ipm->sts & QUARK_SE_IPM_STS_STS_BIT) { } } return 0; } static int quark_se_ipm_max_data_size_get(struct device *d) { ARG_UNUSED(d); return QUARK_SE_IPM_DATA_REGS * sizeof(u32_t); } static u32_t quark_se_ipm_max_id_val_get(struct device *d) { ARG_UNUSED(d); return QUARK_SE_IPM_MAX_ID_VAL; } static void quark_se_ipm_register_callback(struct device *d, ipm_callback_t cb, void *context) { struct quark_se_ipm_driver_data *driver_data = d->driver_data; driver_data->callback = cb; driver_data->callback_ctx = context; } static int quark_se_ipm_set_enabled(struct device *d, int enable) { const struct quark_se_ipm_config_info *config_info = d->config->config_info; if (config_info->direction != QUARK_SE_IPM_INBOUND) { return -EINVAL; } set_channel_irq_state(config_info->channel, enable); return 0; } const struct ipm_driver_api ipm_quark_se_api_funcs = { .send = quark_se_ipm_send, .register_callback = quark_se_ipm_register_callback, .max_data_size_get = quark_se_ipm_max_data_size_get, .max_id_val_get = quark_se_ipm_max_id_val_get, .set_enabled = quark_se_ipm_set_enabled }; int quark_se_ipm_controller_initialize(struct device *d) { const struct quark_se_ipm_controller_config_info *config = d->config->config_info; #if CONFIG_IPM_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_IPM_MASK); for (i = 0; i < QUARK_SE_IPM_CHANNELS; ++i) { volatile struct quark_se_ipm *ipm = QUARK_SE_IPM(i); ipm->sts = 0; } #endif if (config->controller_init) { return config->controller_init(); } return 0; } int quark_se_ipm_initialize(struct device *d) { const struct quark_se_ipm_config_info *config = d->config->config_info; device_by_channel[config->channel] = d; if (config->direction == QUARK_SE_IPM_INBOUND) { inbound_channels |= (0x3 << (config->channel * 2)); } return 0; }