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 <ddk/binding.h>
6 #include <ddk/debug.h>
7 #include <ddk/device.h>
8 #include <ddk/driver.h>
9 #include <ddk/platform-defs.h>
10 #include <ddk/protocol/mailbox.h>
11 #include <ddk/protocol/platform/device.h>
12 #include <fbl/auto_lock.h>
13 #include <fbl/unique_ptr.h>
14 #include <stdint.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <unistd.h>
19 #include <zircon/assert.h>
20 #include <zircon/device/thermal.h>
21 
22 #include "aml-scpi.h"
23 
24 namespace scpi {
25 
GetMailbox(uint32_t cmd,mailbox_type_t * mailbox)26 zx_status_t AmlSCPI::GetMailbox(uint32_t cmd,
27                                 mailbox_type_t* mailbox) {
28     if (!mailbox || !VALID_CMD(cmd)) {
29         return ZX_ERR_INVALID_ARGS;
30     }
31 
32     for (uint32_t i = 0; i < countof(aml_low_priority_cmds); i++) {
33         if (cmd == aml_low_priority_cmds[i]) {
34             *mailbox = MAILBOX_TYPE_AP_NS_LOW_PRIORITY_MAILBOX;
35             return ZX_OK;
36         }
37     }
38 
39     for (uint32_t i = 0; i < countof(aml_high_priority_cmds); i++) {
40         if (cmd == aml_high_priority_cmds[i]) {
41             *mailbox = MAILBOX_TYPE_AP_NS_HIGH_PRIORITY_MAILBOX;
42             return ZX_OK;
43         }
44     }
45 
46     for (uint32_t i = 0; i < countof(aml_secure_cmds); i++) {
47         if (cmd == aml_secure_cmds[i]) {
48             *mailbox = MAILBOX_TYPE_AP_SECURE_MAILBOX;
49             return ZX_OK;
50         }
51     }
52     *mailbox = MAILBOX_TYPE_INVALID_MAILBOX;
53     return ZX_ERR_NOT_FOUND;
54 }
55 
ExecuteCommand(void * rx_buf,size_t rx_size,void * tx_buf,size_t tx_size,uint32_t cmd,uint32_t client_id)56 zx_status_t AmlSCPI::ExecuteCommand(void* rx_buf, size_t rx_size,
57                                     void* tx_buf, size_t tx_size,
58                                     uint32_t cmd, uint32_t client_id) {
59     uint32_t mailbox_status = 0;
60     mailbox_data_buf_t mdata;
61     mdata.cmd = PACK_SCPI_CMD(cmd, client_id, 0);
62     mdata.tx_buffer = tx_buf;
63     mdata.tx_size = tx_size;
64 
65     mailbox_channel_t channel;
66     zx_status_t status = GetMailbox(cmd, &channel.mailbox);
67     if (status != ZX_OK) {
68         SCPI_ERROR("aml_scpi_get_mailbox failed - error status %d\n", status);
69         return status;
70     }
71 
72     channel.rx_buffer = rx_buf;
73     channel.rx_size = rx_size;
74 
75     status = mailbox_.SendCommand(&channel, &mdata);
76     if (rx_buf) {
77         mailbox_status = *(uint32_t*)(rx_buf);
78     }
79     if (status != ZX_OK || mailbox_status != 0) {
80         SCPI_ERROR("mailbox_send_command failed - error status %d\n", status);
81         return status;
82     }
83     return ZX_OK;
84 }
85 
ScpiGetDvfsIdx(uint8_t power_domain,uint16_t * idx)86 zx_status_t AmlSCPI::ScpiGetDvfsIdx(uint8_t power_domain, uint16_t* idx) {
87     struct {
88         uint32_t status;
89         uint8_t idx;
90     } __PACKED aml_dvfs_idx_info;
91 
92     if (!idx || power_domain >= MAX_DVFS_DOMAINS) {
93         return ZX_ERR_INVALID_ARGS;
94     }
95 
96     zx_status_t status = ExecuteCommand(&aml_dvfs_idx_info, sizeof(aml_dvfs_idx_info),
97                                         &power_domain, sizeof(power_domain),
98                                         SCPI_CMD_GET_DVFS, SCPI_CL_DVFS);
99     if (status != ZX_OK) {
100         return status;
101     }
102 
103     *idx = aml_dvfs_idx_info.idx;
104 
105     SCPI_INFO("Current Operation point %x\n", aml_dvfs_idx_info.idx);
106     return ZX_OK;
107 }
108 
ScpiSetDvfsIdx(uint8_t power_domain,uint16_t idx)109 zx_status_t AmlSCPI::ScpiSetDvfsIdx(uint8_t power_domain, uint16_t idx) {
110     struct {
111         uint8_t power_domain;
112         uint16_t idx;
113     } __PACKED aml_dvfs_idx_info;
114 
115     if (power_domain >= MAX_DVFS_DOMAINS) {
116         return ZX_ERR_INVALID_ARGS;
117     }
118 
119     aml_dvfs_idx_info.power_domain = power_domain;
120     aml_dvfs_idx_info.idx = idx;
121 
122     SCPI_INFO("OPP index for cluster %d to %d\n", power_domain, idx);
123     return ExecuteCommand(NULL, 0,
124                           &aml_dvfs_idx_info, sizeof(aml_dvfs_idx_info),
125                           SCPI_CMD_SET_DVFS, SCPI_CL_DVFS);
126 }
127 
ScpiGetDvfsInfo(uint8_t power_domain,scpi_opp_t * out_opps)128 zx_status_t AmlSCPI::ScpiGetDvfsInfo(uint8_t power_domain, scpi_opp_t* out_opps) {
129     zx_status_t status;
130     struct {
131         uint32_t status;
132         uint8_t reserved;
133         uint8_t operating_points;
134         uint16_t latency;
135         scpi_opp_entry_t opp[MAX_DVFS_OPPS];
136     } __PACKED aml_dvfs_info;
137 
138     if (!out_opps || power_domain >= MAX_DVFS_DOMAINS) {
139         return ZX_ERR_INVALID_ARGS;
140     }
141 
142     fbl::AutoLock mailbox_lock(&lock_);
143 
144     // dvfs info already populated
145     if (scpi_opp[power_domain]) {
146         memcpy(out_opps, scpi_opp[power_domain], sizeof(scpi_opp_t));
147         return ZX_OK;
148     }
149 
150     status = ExecuteCommand(&aml_dvfs_info, sizeof(aml_dvfs_info),
151                             &power_domain, sizeof(power_domain),
152                             SCPI_CMD_GET_DVFS_INFO, SCPI_CL_DVFS);
153     if (status != ZX_OK) {
154         return status;
155     }
156 
157     out_opps->count = aml_dvfs_info.operating_points;
158     out_opps->latency = aml_dvfs_info.latency;
159 
160     if (out_opps->count > MAX_DVFS_OPPS) {
161         SCPI_ERROR("Number of operating_points greater than MAX_DVFS_OPPS\n");
162         status = ZX_ERR_INVALID_ARGS;
163         return status;
164     }
165 
166     zxlogf(INFO, "Cluster %u details\n", power_domain);
167     zxlogf(INFO, "Number of operating_points %u\n", aml_dvfs_info.operating_points);
168     zxlogf(INFO, "latency %u uS\n", aml_dvfs_info.latency);
169 
170     for (uint32_t i = 0; i < out_opps->count; i++) {
171         out_opps->opp[i].freq_hz = aml_dvfs_info.opp[i].freq_hz;
172         out_opps->opp[i].volt_mv = aml_dvfs_info.opp[i].volt_mv;
173         zxlogf(INFO, "Operating point %d - ", i);
174         zxlogf(INFO, "Freq %.4f Ghz ", (out_opps->opp[i].freq_hz) / (double)1000000000);
175         zxlogf(INFO, "Voltage %.4f V\n", (out_opps->opp[i].volt_mv) / (double)1000);
176     }
177 
178     scpi_opp[power_domain] = static_cast<scpi_opp_t*>(calloc(1, sizeof(scpi_opp_t)));
179     if (!scpi_opp[power_domain]) {
180         status = ZX_ERR_NO_MEMORY;
181         return status;
182     }
183 
184     memcpy(scpi_opp[power_domain], out_opps, sizeof(scpi_opp_t));
185     return ZX_OK;
186 }
187 
ScpiGetSensorValue(uint32_t sensor_id,uint32_t * sensor_value)188 zx_status_t AmlSCPI::ScpiGetSensorValue(uint32_t sensor_id, uint32_t* sensor_value) {
189     struct {
190         uint32_t status;
191         uint16_t sensor_value;
192     } __PACKED aml_sensor_val;
193 
194     if (!sensor_value) {
195         return ZX_ERR_INVALID_ARGS;
196     }
197 
198     zx_status_t status = ExecuteCommand(&aml_sensor_val, sizeof(aml_sensor_val),
199                                         &sensor_id, sizeof(sensor_id),
200                                         SCPI_CMD_SENSOR_VALUE, SCPI_CL_THERMAL);
201     if (status != ZX_OK) {
202         return status;
203     }
204     *sensor_value = aml_sensor_val.sensor_value;
205     return ZX_OK;
206 }
207 
ScpiGetSensor(const char * name,uint32_t * sensor_value)208 zx_status_t AmlSCPI::ScpiGetSensor(const char* name, uint32_t* sensor_value) {
209     struct {
210         uint32_t status;
211         uint16_t num_sensors;
212     } __PACKED aml_sensor_cap;
213 
214     struct {
215         uint32_t status;
216         uint16_t sensor;
217         uint8_t sensor_class;
218         uint8_t trigger;
219         char sensor_name[20];
220     } __PACKED aml_sensor_info;
221 
222     if (sensor_value == NULL) {
223         return ZX_ERR_INVALID_ARGS;
224     }
225 
226     // First let's find information about all sensors
227     zx_status_t status = ExecuteCommand(&aml_sensor_cap, sizeof(aml_sensor_cap),
228                                         NULL, 0,
229                                         SCPI_CMD_SENSOR_CAPABILITIES, SCPI_CL_THERMAL);
230     if (status != ZX_OK) {
231         return status;
232     }
233 
234     // Loop through all the sensors
235     for (uint32_t sensor_id = 0; sensor_id < aml_sensor_cap.num_sensors; sensor_id++) {
236 
237         status = ExecuteCommand(&aml_sensor_info, sizeof(aml_sensor_info),
238                                 &sensor_id, sizeof(sensor_id),
239                                 SCPI_CMD_SENSOR_INFO, SCPI_CL_THERMAL);
240         if (status != ZX_OK) {
241             return status;
242         }
243         if (!strncmp(name, aml_sensor_info.sensor_name, sizeof(aml_sensor_info.sensor_name))) {
244             *sensor_value = sensor_id;
245             break;
246         }
247     }
248     return ZX_OK;
249 }
250 
DdkUnbind()251 void AmlSCPI::DdkUnbind() {
252     DdkRemove();
253 }
254 
DdkRelease()255 void AmlSCPI::DdkRelease() {
256     delete this;
257 }
258 
Bind()259 zx_status_t AmlSCPI::Bind() {
260     zx_device_prop_t props[] = {
261         {BIND_PLATFORM_DEV_VID, 0, PDEV_VID_AMLOGIC},
262         {BIND_PLATFORM_DEV_PID, 0, PDEV_PID_AMLOGIC_S912},
263         {BIND_PLATFORM_DEV_DID, 0, PDEV_DID_AMLOGIC_THERMAL},
264     };
265 
266     device_add_args_t args = {};
267     args.version = DEVICE_ADD_ARGS_VERSION;
268     args.name = "aml-scpi";
269     args.ctx = this;
270     args.ops = &ddk_device_proto_;
271     args.proto_id = ddk_proto_id_;
272     args.proto_ops = ddk_proto_ops_;
273     args.props = props;
274     args.prop_count = countof(props);
275 
276     return pdev_.DeviceAdd(0, &args, &zxdev_);
277 }
278 
Create(zx_device_t * parent)279 zx_status_t AmlSCPI::Create(zx_device_t* parent) {
280     fbl::AllocChecker ac;
281     auto scpi_device = fbl::make_unique_checked<AmlSCPI>(&ac, parent);
282     if (!ac.check()) {
283         return ZX_ERR_NO_MEMORY;
284     }
285 
286     zx_status_t status = ZX_ERR_INTERNAL;
287     // Get ZX_PROTOCOL_MAILBOX protocol.
288     if (!scpi_device->mailbox_.is_valid()) {
289         zxlogf(ERROR, "dwmac: could not obtain ZX_PROTOCOL_MAILBOX protocol: %d\n", status);
290         return status;
291     }
292 
293     mtx_init(&scpi_device->lock_, mtx_plain);
294 
295     status = scpi_device->Bind();
296     if (status != ZX_OK) {
297         zxlogf(ERROR, "aml-scpi driver failed to get added: %d\n", status);
298         return status;
299     } else {
300         zxlogf(INFO, "aml-scpi driver added\n");
301     }
302 
303     // scpi_device intentionally leaked as it is now held by DevMgr
304     __UNUSED auto ptr = scpi_device.release();
305 
306     return ZX_OK;
307 }
308 
aml_scpi_bind(void * ctx,zx_device_t * parent)309 zx_status_t aml_scpi_bind(void* ctx, zx_device_t* parent) {
310     return scpi::AmlSCPI::Create(parent);
311 }
312 
__anone3c160330702() 313 static zx_driver_ops_t driver_ops = []() {
314     zx_driver_ops_t ops;
315     ops.version = DRIVER_OPS_VERSION;
316     ops.bind = aml_scpi_bind;
317     return ops;
318 }();
319 
320 } // namespace scpi
321 
322 // clang-format off
323 ZIRCON_DRIVER_BEGIN(aml_scpi, scpi::driver_ops, "zircon", "0.1", 4)
324     BI_ABORT_IF(NE, BIND_PROTOCOL, ZX_PROTOCOL_MAILBOX),
325     BI_ABORT_IF(NE, BIND_PLATFORM_DEV_VID, PDEV_VID_AMLOGIC),
326     BI_ABORT_IF(NE, BIND_PLATFORM_DEV_PID, PDEV_PID_AMLOGIC_S912),
327     BI_MATCH_IF(EQ, BIND_PLATFORM_DEV_DID, PDEV_DID_AMLOGIC_SCPI),
328 ZIRCON_DRIVER_END(aml_scpi)
329