1/*
2 * Copyright 2020, Data61
3 * Commonwealth Scientific and Industrial Research Organisation (CSIRO)
4 * ABN 41 687 119 230.
5 *
6 * This software may be distributed and modified according to the terms of
7 * the GNU General Public License version 2. Note that NO WARRANTY is provided.
8 * See "LICENSE_GPLv2.txt" for details.
9 *
10 * @TAG(DATA61_GPL)
11 */
12
13#include <errno.h>
14
15#include <platsupport/reset.h>
16#include <platsupport/plat/reset.h>
17
18/* NVIDIA interface */
19#include <tx2bpmp/bpmp.h> /* struct mrq_reset_request */
20
21extern uint32_t mrq_reset_id_map[];
22
23typedef struct tx2_reset {
24    ps_io_ops_t *io_ops;
25    struct tx2_bpmp *bpmp;
26} tx2_reset_t;
27
28static inline bool check_valid_reset(reset_id_t id)
29{
30    return (RESET_TOP_GTE <= id && id < NRESETS);
31}
32
33static int tx2_reset_common(void *data, reset_id_t id, bool assert)
34{
35    if (!check_valid_reset(id)) {
36        ZF_LOGE("Invalid reset ID");
37        return -EINVAL;
38    }
39
40    tx2_reset_t *reset = data;
41    uint32_t bpmp_reset_id = mrq_reset_id_map[id];
42
43    /* Setup a message and make a call to BPMP */
44    struct mrq_reset_request req = { .reset_id = bpmp_reset_id };
45    req.cmd = (assert) ? CMD_RESET_ASSERT : CMD_RESET_DEASSERT;
46
47    int bytes_recvd = tx2_bpmp_call(reset->bpmp, MRQ_RESET, &req, sizeof(req), NULL, 0);
48    if (bytes_recvd < 0) {
49        return -EIO;
50    }
51
52    return 0;
53}
54
55static int tx2_reset_assert(void *data, reset_id_t id)
56{
57    return tx2_reset_common(data, id, true);
58}
59
60static int tx2_reset_deassert(void *data, reset_id_t id)
61{
62    return tx2_reset_common(data, id, false);
63}
64
65static int interface_search_handler(void *handler_data, void *interface_instance, char **properties)
66{
67    /* Select the first one that is registered */
68    tx2_reset_t *reset = handler_data;
69    reset->bpmp = (struct tx2_bpmp *) interface_instance;
70    return PS_INTERFACE_FOUND_MATCH;
71}
72
73int reset_sys_init(ps_io_ops_t *io_ops, void *dependencies, reset_sys_t *reset_sys)
74{
75    if (!io_ops || !reset_sys) {
76        if (!io_ops) {
77            ZF_LOGE("null io_ops argument");
78        }
79
80        if (!reset_sys) {
81            ZF_LOGE("null reset_sys argument");
82        }
83
84        return -EINVAL;
85    }
86
87    int error = 0;
88    tx2_reset_t *reset = NULL;
89    error = ps_calloc(&io_ops->malloc_ops, 1, sizeof(tx2_reset_t), (void **) &reset_sys->data);
90    if (error) {
91        ZF_LOGE("Failed to allocate memory for reset sys internal structure");
92        error = -ENOMEM;
93        goto fail;
94    }
95
96    reset = reset_sys->data;
97
98    if (dependencies) {
99        reset->bpmp = (struct tx2_bpmp *) dependencies;
100    } else {
101        /* See if there's a registered interface for the BPMP, if not, then we
102         * initialise one ourselves. */
103        error = ps_interface_find(&io_ops->interface_registration_ops, TX2_BPMP_INTERFACE,
104                                  interface_search_handler, reset);
105        if (error) {
106            error = ps_calloc(&io_ops->malloc_ops, 1, sizeof(struct tx2_bpmp), (void **) &reset->bpmp);
107            if (error) {
108                ZF_LOGE("Failed to allocate memory for the BPMP structure to be initialised");
109                goto fail;
110            }
111
112            error = tx2_bpmp_init(io_ops, reset->bpmp);
113            if (error) {
114                ZF_LOGE("Failed to initialise the BPMP");
115                goto fail;
116            }
117        }
118    }
119
120    reset_sys->reset_assert = &tx2_reset_assert;
121    reset_sys->reset_deassert = &tx2_reset_deassert;
122
123    return 0;
124
125fail:
126
127    if (reset_sys->data) {
128        if (reset->bpmp) {
129            ZF_LOGF_IF(ps_free(&io_ops->malloc_ops, sizeof(struct tx2_bpmp), (void *) reset->bpmp),
130                       "Failed to free the BPMP structure after a failed reset subsystem initialisation");
131        }
132        ZF_LOGF_IF(ps_free(&io_ops->malloc_ops, sizeof(tx2_reset_t), (void *) reset_sys->data),
133                   "Failed to free the reset private data after a failed reset subsystem initialisation");
134    }
135
136    return error;
137}
138