summaryrefslogtreecommitdiff
path: root/components/drivers/firmware/arm_scmi/shmem.c
blob: 876398471e86811b5fa9241cf1c17b86099e326f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
/*
 * Copyright (c) 2006-2022, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2022-11-26     GuEe-GUI     first version
 */

#define DBG_TAG "scmi.shmem"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>

#include "shmem.h"

struct scmi_shared_mem
{
    rt_le32_t reserved;
    rt_le32_t channel_status;
#define SCMI_SHMEM_CHAN_STAT_CHANNEL_ERROR  rt_cpu_to_le32(RT_BIT(1))
#define SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE   rt_cpu_to_le32(RT_BIT(0))
    rt_le32_t reserved1[2];
    rt_le32_t flags;
#define SCMI_SHMEM_FLAG_INTR_ENABLED        rt_cpu_to_le32(RT_BIT(0))
    rt_le32_t length;
    rt_le32_t msg_header;
    rt_uint8_t msg_payload[];
};

rt_err_t scmi_shmem_msg_write(struct scmi_shared_mem *shmem,
        struct rt_scmi_msg *msg)
{
    if (!shmem || !msg)
    {
        return -RT_EINVAL;
    }

    if ((!msg->in_msg && msg->in_msg_size) || (!msg->out_msg && msg->out_msg_size))
    {
        return -RT_EINVAL;
    }

    if (!(shmem->channel_status & SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE))
    {
        LOG_E("Channel busy");

        return -RT_EBUSY;
    }

    /* Load message in shared memory */
    shmem->channel_status &= ~SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE;
    shmem->length = rt_cpu_to_le32(msg->in_msg_size + sizeof(shmem->msg_header));
    shmem->msg_header = rt_cpu_to_le32(scmi_header(
            msg->message_id, 0, msg->sdev->protocol_id, 0));

    if (msg->in_msg)
    {
        rt_memcpy(shmem->msg_payload, msg->in_msg, msg->in_msg_size);
    }

    return RT_EOK;
}

rt_err_t scmi_shmem_msg_read(struct scmi_shared_mem *shmem, struct rt_scmi_msg *msg)
{
    rt_size_t length;

    if (!shmem || !msg)
    {
        return -RT_EINVAL;
    }

    if (!(shmem->channel_status & SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE))
    {
        LOG_E("Channel unexpectedly busy");

        return -RT_EBUSY;
    }

    if (shmem->channel_status & SCMI_SHMEM_CHAN_STAT_CHANNEL_ERROR)
    {
        LOG_E("Channel error reported, reset channel");

        return -RT_EIO;
    }

    length = rt_le32_to_cpu(shmem->length);

    if (length > msg->out_msg_size + sizeof(shmem->msg_header))
    {
        LOG_E("Buffer < %u too small", length);

        return -RT_EINVAL;
    }

    msg->out_msg_size = length - sizeof(shmem->msg_header);

    rt_memcpy(msg->out_msg, shmem->msg_payload, msg->out_msg_size);

    return RT_EOK;
}

void scmi_shmem_clear_channel(struct scmi_shared_mem *shmem)
{
    if (shmem)
    {
        shmem->channel_status &= ~SCMI_SHMEM_CHAN_STAT_CHANNEL_ERROR;
    }
}