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 <fbl/auto_lock.h>
9 #include <fbl/unique_ptr.h>
10 #include <stdint.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <unistd.h>
15 
16 namespace mailbox {
17 
GetRxMailbox(mailbox_type_t tx_mailbox)18 mailbox_type_t AmlMailbox::GetRxMailbox(mailbox_type_t tx_mailbox) {
19     switch (tx_mailbox) {
20     case MAILBOX_TYPE_AP_SECURE_MAILBOX:
21         return MAILBOX_TYPE_SCP_SECURE_MAILBOX;
22     case MAILBOX_TYPE_AP_NS_LOW_PRIORITY_MAILBOX:
23         return MAILBOX_TYPE_SCP_NS_LOW_PRIORITY_MAILBOX;
24     case MAILBOX_TYPE_AP_NS_HIGH_PRIORITY_MAILBOX:
25         return MAILBOX_TYPE_SCP_NS_HIGH_PRIORITY_MAILBOX;
26     default:
27         return MAILBOX_TYPE_INVALID_MAILBOX;
28     }
29 }
30 
GetNumWords(size_t size)31 size_t AmlMailbox::GetNumWords(size_t size) {
32     return (size / 4 + ((size % 4) ? 1 : 0));
33 }
34 
DdkUnbind()35 void AmlMailbox::DdkUnbind() {
36     DdkRemove();
37 }
38 
DdkRelease()39 void AmlMailbox::DdkRelease() {
40     delete this;
41 }
42 
MailboxSendCommand(const mailbox_channel_t * channel,const mailbox_data_buf_t * mdata)43 zx_status_t AmlMailbox::MailboxSendCommand(const mailbox_channel_t* channel,
44                                            const mailbox_data_buf_t* mdata) {
45     if (!channel || !mdata) {
46         return ZX_ERR_INVALID_ARGS;
47     }
48 
49     mailbox_type_t rx_mailbox_id;
50     if (MAILBOX_TYPE_INVALID_MAILBOX == (rx_mailbox_id = GetRxMailbox(channel->mailbox))) {
51         return ZX_ERR_INVALID_ARGS;
52     }
53 
54     fbl::AutoLock mailbox_lock(&mailbox_chan_lock_[channel->mailbox]);
55 
56     aml_mailbox_block_t* rx_mailbox = &vim2_mailbox_block[rx_mailbox_id];
57     aml_mailbox_block_t* tx_mailbox = &vim2_mailbox_block[channel->mailbox];
58 
59     if (mdata->tx_size != 0) {
60         size_t num = GetNumWords(mdata->tx_size);
61         uint32_t* tx_payload = (uint32_t*)(mdata->tx_buffer);
62         for (size_t i = 0; i < num; i++) {
63             // AP writes parameters to Payload
64             mailbox_payload_mmio_->Write32(tx_payload[i], tx_mailbox->payload_offset + (i << 2));
65         }
66     }
67 
68     // AP writes command to AP Mailbox
69     mailbox_mmio_->Write32(mdata->cmd, tx_mailbox->set_offset);
70 
71     zx_status_t status = inth_[rx_mailbox_id].wait(nullptr);
72     if (status != ZX_OK) {
73         zxlogf(ERROR, "aml-mailbox: zx_interrupt_wait failed: %d\n", status);
74         return status;
75     }
76 
77     // AP reads the Payload to get requested information
78     if (channel->rx_size != 0) {
79         size_t num = GetNumWords(channel->rx_size);
80         uint32_t* rx_payload = (uint32_t*)(channel->rx_buffer);
81         for (size_t i = 0; i < num; i++) {
82             rx_payload[i] = mailbox_payload_mmio_->Read32(rx_mailbox->payload_offset + (i << 2));
83         }
84     }
85 
86     // AP writes to the Mailbox CLR register
87     mailbox_mmio_->Write32(1, rx_mailbox->clr_offset);
88     return ZX_OK;
89 }
90 
Bind()91 zx_status_t AmlMailbox::Bind() {
92 
93     zx_device_prop_t props[] = {
94         {BIND_PLATFORM_DEV_VID, 0, PDEV_VID_AMLOGIC},
95         {BIND_PLATFORM_DEV_PID, 0, PDEV_PID_AMLOGIC_S912},
96         {BIND_PLATFORM_DEV_DID, 0, PDEV_DID_AMLOGIC_SCPI},
97     };
98 
99     device_add_args_t args = {};
100     args.version = DEVICE_ADD_ARGS_VERSION;
101     args.name = "aml-mailbox";
102     args.ctx = this;
103     args.ops = &ddk_device_proto_;
104     args.proto_id = ddk_proto_id_;
105     args.proto_ops = ddk_proto_ops_;
106     args.props = props;
107     args.prop_count = countof(props);
108 
109     return pdev_.DeviceAdd(0, &args, &zxdev_);
110 }
111 
InitPdev()112 zx_status_t AmlMailbox::InitPdev() {
113     if (!pdev_.is_valid()) {
114         return ZX_ERR_NO_RESOURCES;
115     }
116 
117     // Map MMIOs
118     zx_status_t status = pdev_.MapMmio(MMIO_MAILBOX, &mailbox_mmio_);
119     if (status != ZX_OK) {
120         zxlogf(ERROR, "aml-mailbox: could not map mailbox mmio: %d\n", status);
121         return status;
122     }
123 
124     status = pdev_.MapMmio(MMIO_MAILBOX_PAYLOAD, &mailbox_payload_mmio_);
125     if (status != ZX_OK) {
126         zxlogf(ERROR, "aml-mailbox: could not map payload mmio: %d\n", status);
127         return status;
128     }
129 
130     for (uint32_t i = 0; i < kNumMailboxes; i++) {
131         status = pdev_.GetInterrupt(i, &inth_[i]);
132         if (status != ZX_OK) {
133             zxlogf(ERROR, "aml-mailbox: could not map interrupt: %d\n", status);
134             return status;
135         }
136 
137         mtx_init(&mailbox_chan_lock_[i], mtx_plain);
138     }
139 
140     return status;
141 }
142 
Create(zx_device_t * parent)143 zx_status_t AmlMailbox::Create(zx_device_t* parent) {
144     fbl::AllocChecker ac;
145     auto mailbox_device = fbl::make_unique_checked<AmlMailbox>(&ac, parent);
146     if (!ac.check()) {
147         return ZX_ERR_NO_MEMORY;
148     }
149 
150     zx_status_t status = mailbox_device->InitPdev();
151     if (status != ZX_OK) {
152         return status;
153     }
154 
155     status = mailbox_device->Bind();
156     if (status != ZX_OK) {
157         zxlogf(ERROR, "aml-mailbox driver failed to get added: %d\n", status);
158         return status;
159     } else {
160         zxlogf(INFO, "aml-mailbox driver added\n");
161     }
162 
163     // mailbox_device intentionally leaked as it is now held by DevMgr
164     __UNUSED auto ptr = mailbox_device.release();
165 
166     return ZX_OK;
167 }
168 
aml_mailbox_bind(void * ctx,zx_device_t * parent)169 zx_status_t aml_mailbox_bind(void* ctx, zx_device_t* parent) {
170     return mailbox::AmlMailbox::Create(parent);
171 }
172 
__anon393ee2f00102() 173 static zx_driver_ops_t driver_ops = []() {
174     zx_driver_ops_t ops;
175     ops.version = DRIVER_OPS_VERSION;
176     ops.bind = aml_mailbox_bind;
177     return ops;
178 }();
179 
180 } // namespace mailbox
181 
182 // clang-format off
183 ZIRCON_DRIVER_BEGIN(aml_mailbox, mailbox::driver_ops, "zircon", "0.1", 4)
184     BI_ABORT_IF(NE, BIND_PROTOCOL, ZX_PROTOCOL_PDEV),
185     BI_ABORT_IF(NE, BIND_PLATFORM_DEV_VID, PDEV_VID_KHADAS),
186     BI_ABORT_IF(NE, BIND_PLATFORM_DEV_PID, PDEV_PID_VIM2),
187     BI_MATCH_IF(EQ, BIND_PLATFORM_DEV_DID, PDEV_DID_AMLOGIC_MAILBOX),
188 ZIRCON_DRIVER_END(aml_mailbox)
189