/* * Copyright (c) 2021-2023, Arm Limited and Contributors. All rights reserved. * Copyright (c) 2021-2023, Linaro Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #include #include #include #include #include #include #include #include #include #include #include "openamp_messenger_api.h" #define MHU_V_2_NOTIFY_CHANNEL 0 #define MHU_V_2_NOTIFY_VALUE 0xff struct openamp_mhu { struct device_region rx_region; struct device_region tx_region; struct mhu_v2_x_dev_t rx_dev; struct mhu_v2_x_dev_t tx_dev; }; static int openamp_mhu_device_get(const char *dev, struct device_region *dev_region) { bool found; found = config_store_query(CONFIG_CLASSIFIER_DEVICE_REGION, dev, 0, dev_region, sizeof(*dev_region)); if (!found) return -EINVAL; if (!dev_region->base_addr) return -EINVAL; IMSG("mhu: device region found: %s addr: 0x%p size: %lu", dev, (void *)dev_region->base_addr, dev_region->io_region_size); return 0; } int openamp_mhu_receive(struct openamp_messenger *openamp) { struct mhu_v2_x_dev_t *rx_dev; enum mhu_v2_x_error_t ret; struct openamp_mhu *mhu; uint32_t channel = 0; uint32_t irq_status; if (!openamp->transport) { EMSG("openamp: mhu: receive transport not initialized"); return -EINVAL; } mhu = openamp->transport; rx_dev = &mhu->rx_dev; irq_status = 0; do { irq_status = mhu_v2_x_get_interrupt_status(rx_dev); } while(!irq_status); ret = mhu_v2_1_get_ch_interrupt_num(rx_dev, &channel); ret = mhu_v2_x_channel_clear(rx_dev, channel); if (ret != MHU_V_2_X_ERR_NONE) { EMSG("openamp: mhu: failed to clear channel: %d", channel); return -EPROTO; } return 0; } int openamp_mhu_notify_peer(struct openamp_messenger *openamp) { struct mhu_v2_x_dev_t *tx_dev; enum mhu_v2_x_error_t ret; struct openamp_mhu *mhu; uint32_t access_ready; if (!openamp->transport) { EMSG("openamp: mhu: notify transport not initialized"); return -EINVAL; } mhu = openamp->transport; tx_dev = &mhu->tx_dev; ret = mhu_v2_x_set_access_request(tx_dev); if (ret != MHU_V_2_X_ERR_NONE) { EMSG("openamp: mhu: set access request failed"); return -EPROTO; } do { ret = mhu_v2_x_get_access_ready(tx_dev, &access_ready); if (ret != MHU_V_2_X_ERR_NONE) { EMSG("openamp: mhu: failed to get access_ready"); return -EPROTO; } } while (!access_ready); ret = mhu_v2_x_channel_send(tx_dev, MHU_V_2_NOTIFY_CHANNEL, MHU_V_2_NOTIFY_VALUE); if (ret != MHU_V_2_X_ERR_NONE) { EMSG("openamp: mhu: failed send over channel"); return -EPROTO; } ret = mhu_v2_x_reset_access_request(tx_dev); if (ret != MHU_V_2_X_ERR_NONE) { EMSG("openamp: mhu: failed reset access request"); return -EPROTO; } return 0; } int openamp_mhu_init(struct openamp_messenger *openamp) { struct mhu_v2_x_dev_t *rx_dev; struct mhu_v2_x_dev_t *tx_dev; struct openamp_mhu *mhu; int ret; /* if we already have initialized skip this */ if (openamp->transport) return 0; mhu = malloc(sizeof(*mhu)); if (!mhu) return -1; ret = openamp_mhu_device_get("mhu-sender", &mhu->tx_region); if (ret < 0) goto free_mhu; ret = openamp_mhu_device_get("mhu-receiver", &mhu->rx_region); if (ret < 0) goto free_mhu; rx_dev = &mhu->rx_dev; tx_dev = &mhu->tx_dev; rx_dev->base = mhu->rx_region.base_addr; rx_dev->frame = MHU_V2_X_RECEIVER_FRAME; tx_dev->base = mhu->tx_region.base_addr; tx_dev->frame = MHU_V2_X_SENDER_FRAME; ret = mhu_v2_x_driver_init(rx_dev, MHU_REV_READ_FROM_HW); if (ret < 0) goto free_mhu; ret = mhu_v2_x_driver_init(tx_dev, MHU_REV_READ_FROM_HW); if (ret < 0) goto free_mhu; openamp->transport = (void *)mhu; return 0; free_mhu: free(mhu); return ret; } int openamp_mhu_deinit(struct openamp_messenger *openamp) { struct openamp_mhu *mhu; if (!openamp->transport) return 0; mhu = openamp->transport; free(mhu); openamp->transport = NULL; return 0; }