1// Copyright 2018 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "aml-mailbox.h"
6#include "aml-mailbox-hw.h"
7#include <ddk/binding.h>
8#include <stdint.h>
9#include <stdio.h>
10#include <stdlib.h>
11#include <string.h>
12#include <unistd.h>
13
14#define READ32_MAILBOX_PL_REG(offset) \
15        readl((uint32_t*)mailbox->mmio_mailbox_payload.vaddr + (offset))
16#define WRITE32_MAILBOX_PL_REG(offset, value) \
17        writel(value, (uint32_t*)mailbox->mmio_mailbox_payload.vaddr + (offset))
18#define READ32_MAILBOX_REG(offset) \
19        readl((uint32_t*)mailbox->mmio_mailbox.vaddr + (offset))
20#define WRITE32_MAILBOX_REG(offset, value) \
21        writel(value, (uint32_t*)mailbox->mmio_mailbox.vaddr + (offset))
22
23static int aml_get_rx_mailbox(uint32_t tx_mailbox) {
24    switch (tx_mailbox) {
25    case AP_SECURE_MAILBOX:
26        return SCP_SECURE_MAILBOX;
27    case AP_NS_LOW_PRIORITY_MAILBOX:
28        return SCP_NS_LOW_PRIORITY_MAILBOX;
29    case AP_NS_HIGH_PRIORITY_MAILBOX:
30        return SCP_NS_HIGH_PRIORITY_MAILBOX;
31    default:
32        return INVALID_MAILBOX;
33    }
34}
35
36static zx_status_t aml_mailbox_send_cmd(void* ctx,
37                                        mailbox_channel_t* channel,
38                                        mailbox_data_buf_t* mdata) {
39    aml_mailbox_t* mailbox = ctx;
40    int rx_mailbox_id;
41    if (!channel || !mdata) {
42        return ZX_ERR_INVALID_ARGS;
43    }
44
45    if (INVALID_MAILBOX == (rx_mailbox_id =
46                                aml_get_rx_mailbox(channel->mailbox))) {
47        return ZX_ERR_INVALID_ARGS;
48    }
49
50    mtx_lock(&mailbox->mailbox_chan_lock[channel->mailbox]);
51    aml_mailbox_block_t* rx_mailbox = &vim2_mailbox_block[rx_mailbox_id];
52    aml_mailbox_block_t* tx_mailbox = &vim2_mailbox_block[channel->mailbox];
53
54    if (mdata->tx_size != 0) {
55        uint32_t num = GET_NUM_WORDS(mdata->tx_size);
56        uint32_t* tx_payload = (uint32_t*)(mdata->tx_buf);
57        for (uint32_t i = 0; i < num; i++) {
58            // AP writes parameters to Payload
59            WRITE32_MAILBOX_PL_REG(tx_mailbox->payload_offset + i, tx_payload[i]);
60        }
61    }
62
63    // AP writes command to AP Mailbox
64    WRITE32_MAILBOX_REG(tx_mailbox->set_offset, mdata->cmd);
65
66    zx_status_t status = zx_interrupt_wait(mailbox->inth[rx_mailbox_id], NULL);
67    if (status != ZX_OK) {
68        MAILBOX_ERROR("zx_interrupt_wait failed\n");
69        mtx_unlock(&mailbox->mailbox_chan_lock[channel->mailbox]);
70        return status;
71    }
72
73    // AP reads the Payload to get requested information
74    if (channel->rx_size != 0) {
75        uint32_t num = GET_NUM_WORDS(channel->rx_size);
76        uint32_t* rx_payload = (uint32_t*)(channel->rx_buf);
77        for (uint32_t i = 0; i < num; i++) {
78            rx_payload[i] = READ32_MAILBOX_PL_REG(rx_mailbox->payload_offset + i);
79        }
80    }
81
82    // AP writes to the Mailbox CLR register
83    WRITE32_MAILBOX_REG(rx_mailbox->clr_offset, 1);
84
85    mtx_unlock(&mailbox->mailbox_chan_lock[channel->mailbox]);
86    return ZX_OK;
87}
88
89static void aml_mailbox_release(void* ctx) {
90    aml_mailbox_t* mailbox = ctx;
91    mmio_buffer_release(&mailbox->mmio_mailbox);
92    mmio_buffer_release(&mailbox->mmio_mailbox_payload);
93    for (uint32_t i = 0; i < NUM_MAILBOXES; i++) {
94        zx_interrupt_destroy(mailbox->inth[i]);
95        zx_handle_close(mailbox->inth[i]);
96    }
97    free(mailbox);
98}
99
100static zx_protocol_device_t aml_mailbox_device_protocol = {
101    .version = DEVICE_OPS_VERSION,
102    .release = aml_mailbox_release,
103};
104
105static mailbox_protocol_ops_t mailbox_ops = {
106    .send_cmd = aml_mailbox_send_cmd,
107};
108
109static zx_status_t aml_mailbox_bind(void* ctx, zx_device_t* parent) {
110    zx_status_t status = ZX_OK;
111
112    aml_mailbox_t* mailbox = calloc(1, sizeof(aml_mailbox_t));
113    if (!mailbox) {
114        return ZX_ERR_NO_MEMORY;
115    }
116
117    status = device_get_protocol(parent, ZX_PROTOCOL_PLATFORM_DEV, &mailbox->pdev);
118    if (status != ZX_OK) {
119        MAILBOX_ERROR("Could not get parent protocol\n");
120        goto fail;
121    }
122
123    pdev_device_info_t info;
124    status = pdev_get_device_info(&mailbox->pdev, &info);
125    if (status != ZX_OK) {
126        MAILBOX_ERROR("pdev_get_device_info failed\n");
127        goto fail;
128    }
129
130    // Map all MMIOs
131    status = pdev_map_mmio_buffer2(&mailbox->pdev, MMIO_MAILBOX,
132                                  ZX_CACHE_POLICY_UNCACHED_DEVICE,
133                                  &mailbox->mmio_mailbox);
134    if (status != ZX_OK) {
135        MAILBOX_ERROR("Could not map mailbox MMIO_MAILBOX %d\n", status);
136        goto fail;
137    }
138
139    status = pdev_map_mmio_buffer2(&mailbox->pdev, MMIO_MAILBOX_PAYLOAD,
140                                  ZX_CACHE_POLICY_UNCACHED_DEVICE,
141                                  &mailbox->mmio_mailbox_payload);
142    if (status != ZX_OK) {
143        MAILBOX_ERROR("Could not map mailbox MMIO_MAILBOX_PAYLOAD %d\n", status);
144        goto fail;
145    }
146
147    for (uint32_t i = 0; i < NUM_MAILBOXES; i++) {
148        status = pdev_map_interrupt(&mailbox->pdev, i,
149                                    &mailbox->inth[i]);
150        if (status != ZX_OK) {
151            MAILBOX_ERROR("pdev_map_interrupt failed %d\n", status);
152            goto fail;
153        }
154
155        mtx_init(&mailbox->mailbox_chan_lock[i], mtx_plain);
156    }
157
158    zx_device_prop_t props[] = {
159        {BIND_PLATFORM_DEV_VID, 0, PDEV_VID_AMLOGIC},
160        {BIND_PLATFORM_DEV_PID, 0, PDEV_PID_AMLOGIC_S912},
161        {BIND_PLATFORM_DEV_DID, 0, PDEV_DID_AMLOGIC_SCPI},
162    };
163
164    device_add_args_t args = {
165        .version = DEVICE_ADD_ARGS_VERSION,
166        .name = "aml-mailbox",
167        .ctx = mailbox,
168        .ops = &aml_mailbox_device_protocol,
169        .proto_id = ZX_PROTOCOL_MAILBOX,
170        .proto_ops = &mailbox_ops,
171        .props = props,
172        .prop_count = countof(props),
173    };
174
175    status = pdev_device_add(&mailbox->pdev, 0, &args, NULL);
176    if (status != ZX_OK) {
177        goto fail;
178    }
179
180    return ZX_OK;
181fail:
182    aml_mailbox_release(mailbox);
183    return ZX_OK;
184}
185
186static zx_driver_ops_t aml_mailbox_driver_ops = {
187    .version = DRIVER_OPS_VERSION,
188    .bind = aml_mailbox_bind,
189};
190
191ZIRCON_DRIVER_BEGIN(aml_mailbox, aml_mailbox_driver_ops, "zircon", "0.1", 4)
192    BI_ABORT_IF(NE, BIND_PROTOCOL, ZX_PROTOCOL_PLATFORM_DEV),
193    BI_ABORT_IF(NE, BIND_PLATFORM_DEV_VID, PDEV_VID_KHADAS),
194    BI_ABORT_IF(NE, BIND_PLATFORM_DEV_PID, PDEV_PID_VIM2),
195    BI_MATCH_IF(EQ, BIND_PLATFORM_DEV_DID, PDEV_DID_AMLOGIC_MAILBOX),
196ZIRCON_DRIVER_END(aml_mailbox)
197