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