1/*
2 * Copyright (c) 2016, NVIDIA CORPORATION.
3 * Copright 2019, Data61
4 * Commonwealth Scientific and Industrial Research Organisation (CSIRO)
5 * ABN 41 687 119 230.
6 *
7 * This software may be distributed and modified according to the terms of
8 * the GNU General Public License version 2. Note that NO WARRANTY is provided.
9 * See "LICENSE_GPLv2.txt" for details.
10 *
11 * @TAG(DATA61_GPL)
12 */
13
14#include <assert.h>
15#include <errno.h>
16#include <stdbool.h>
17
18#include <platsupport/pmem.h>
19#include <platsupport/fdt.h>
20#include <tx2bpmp/hsp.h>
21#include <utils/util.h>
22
23/* Register holds information about the number of
24 * shared mailboxes, shared semaphores etc. */
25#define HSP_INT_DIMENSION_OFFSET 0x380
26#define HSP_INT_DIMENSION_SM_SHIFT 0
27#define HSP_INT_DIMENSION_SS_SHIFT 4
28#define HSP_INT_DIMENSION_AS_SHIFT 8
29#define HSP_INT_DIMENSION_NUM_MASK 0xf
30
31#define HSP_DOORBELL_BLOCK_STRIDE 0x100
32
33#define HSP_BITMAP_TZ_SECURE_SHFIT 0
34#define HSP_BITMAP_TZ_NONSECURE_SHIFT 16
35
36typedef struct tx2_hsp_priv {
37    ps_io_ops_t *io_ops;
38    void *hsp_base;
39    void *doorbell_base;
40    pmem_region_t tx2_hsp_region;
41} tx2_hsp_priv_t;
42
43enum dbell_reg_offset {
44    DBELL_TRIGGER = 0x0,
45    DBELL_ENABLE = 0x4,
46    DBELL_RAW = 0x8,
47    DBELL_PENDING = 0xc
48};
49
50enum dbell_bitmap_offset {
51    CCPLEX_BIT = BIT(1),
52    DPMU_BIT = BIT(2),
53    BPMP_BIT = BIT(3),
54    SPE_BIT = BIT(4),
55    CPE_BIT = BIT(5),
56    SCE_BIT = CPE_BIT,
57    DMA_BIT = BIT(6),
58    TSECA_BIT = BIT(7),
59    TSECB_BIT = BIT(8),
60    JTAGM_BIT = BIT(9),
61    CSITE_BIT = BIT(10),
62    APE_BIT = BIT(11)
63};
64
65
66static bool check_doorbell_id_is_valid(enum tx2_doorbell_id db_id)
67{
68    if (CCPLEX_PM_DBELL <= db_id && db_id <= APE_DBELL) {
69        return true;
70    }
71    return false;
72}
73
74static uint32_t *hsp_get_doorbell_register(tx2_hsp_priv_t *hsp, enum tx2_doorbell_id db_id,
75                                           enum dbell_reg_offset offset)
76{
77    assert(hsp);
78    assert(DBELL_TRIGGER <= offset && offset <= DBELL_PENDING);
79    return hsp->doorbell_base + db_id * HSP_DOORBELL_BLOCK_STRIDE + offset;
80}
81
82static int hsp_destroy(void *data)
83{
84    tx2_hsp_priv_t *hsp_priv = data;
85
86    /* The doorbell base is just an offset from the hsp base, so we only need
87     * to deallocate the hsp base */
88    if (hsp_priv->hsp_base) {
89        ps_io_unmap(&hsp_priv->io_ops->io_mapper, hsp_priv->hsp_base, hsp_priv->tx2_hsp_region.length);
90    }
91
92    ps_io_ops_t *temp_ops = hsp_priv->io_ops;
93
94    ZF_LOGF_IF(ps_free(&temp_ops->malloc_ops, sizeof(*hsp_priv), hsp_priv),
95               "Failed to de-allocate the private data for HSP");
96
97    return 0;
98}
99
100static int hsp_doorbell_ring(void *data, enum tx2_doorbell_id db_id)
101{
102    if (!check_doorbell_id_is_valid(db_id)) {
103        ZF_LOGE("Invalid doorbell ID!");
104        return -EINVAL;
105    }
106
107    tx2_hsp_priv_t *hsp_priv = data;
108
109    /* Write any value to the trigger register to 'ring' the doorbell */
110    uint32_t *trigger_reg = hsp_get_doorbell_register(hsp_priv, db_id, DBELL_TRIGGER);
111    assert(trigger_reg);
112    *trigger_reg = 1;
113
114    return 0;
115}
116
117static int hsp_doorbell_check(void *data, enum tx2_doorbell_id db_id)
118{
119    if (!check_doorbell_id_is_valid(db_id)) {
120        ZF_LOGE("Invalid doorbell ID!");
121        return -EINVAL;
122    }
123
124    tx2_hsp_priv_t *hsp_priv = data;
125
126    /* Checking if the doorbell has been 'rung' requires checking for proper
127     * bit in the bitfield. The bitfield is also split into TrustZone secure
128     * and TZ non-secure. Refer to Figure 75 in Section 14.8.5 for further details. */
129    uint32_t *pending_reg = hsp_get_doorbell_register(hsp_priv, db_id, DBELL_PENDING);
130
131    enum dbell_bitmap_offset bitmap_offset;
132    switch (db_id) {
133    case CCPLEX_PM_DBELL:
134    case CCPLEX_TZ_UNSECURE_DBELL:
135    case CCPLEX_TZ_SECURE_DBELL:
136        bitmap_offset = CCPLEX_BIT;
137        break;
138    case BPMP_DBELL:
139        bitmap_offset = BPMP_BIT;
140        break;
141    case SPE_DBELL:
142        bitmap_offset = SPE_BIT;
143        break;
144    case SCE_DBELL:
145        bitmap_offset = SCE_BIT;
146        break;
147    case APE_DBELL:
148        bitmap_offset = APE_BIT;
149        break;
150    default:
151        ZF_LOGF("We shouldn't get here, doorbell ID is %d", db_id);
152    }
153
154    /* Usermode isn't in TrustZone secure, so we just default to TZ non-secure */
155    int is_pending = *pending_reg & (bitmap_offset << HSP_BITMAP_TZ_NONSECURE_SHIFT);
156
157    if (is_pending) {
158        *pending_reg &= ~(bitmap_offset << HSP_BITMAP_TZ_NONSECURE_SHIFT);
159    }
160
161    return (is_pending != 0);
162}
163
164static int allocate_register_callback(pmem_region_t pmem, unsigned curr_num, size_t num_regs, void *token)
165{
166    assert(token != NULL);
167    tx2_hsp_priv_t *hsp_priv = token;
168    /* There's only one register region to map, map it in */
169    assert(num_regs == 1 && curr_num == 0);
170    hsp_priv->hsp_base = ps_pmem_map(hsp_priv->io_ops, pmem, false, PS_MEM_NORMAL);
171    if (hsp_priv->hsp_base == NULL) {
172        return -EIO;
173    }
174    hsp_priv->tx2_hsp_region = pmem;
175    return 0;
176}
177
178
179int tx2_hsp_init(ps_io_ops_t *io_ops, tx2_hsp_t *hsp, const char *path)
180{
181    if (!io_ops || !hsp) {
182        ZF_LOGE("Arguments are NULL!");
183        return -EINVAL;
184    }
185
186    int error = 0;
187
188    tx2_hsp_priv_t *hsp_priv = NULL;
189    error = ps_calloc(&io_ops->malloc_ops, 1, sizeof(*hsp_priv), (void **) &hsp_priv);
190    if (error) {
191        ZF_LOGE("Failed to allocate memory for private data for the HSP");
192        return -ENOMEM;
193    }
194    hsp_priv->io_ops = io_ops;
195
196    ps_fdt_cookie_t *cookie = NULL;
197    error = ps_fdt_read_path(&io_ops->io_fdt, &io_ops->malloc_ops, path, &cookie);
198    if (error) {
199        ZF_LOGE("Failed to find %s in device tree", path);
200        return -ENODEV;
201    }
202
203    /* walk the registers and allocate them */
204    error = ps_fdt_walk_registers(&io_ops->io_fdt, cookie, allocate_register_callback, hsp_priv);
205    if (error) {
206        ZF_LOGE("Failed to walk fdt node");
207        return -ENODEV;
208    }
209
210    if (!hsp_priv->hsp_base) {
211        ZF_LOGE("Failed to map tx2 HSP module");
212        ZF_LOGF_IF(ps_free(&io_ops->malloc_ops, sizeof(*hsp_priv), hsp_priv),
213                   "Failed to clean-up after a failed initialisation for the HSP");
214        return -ENOMEM;
215    }
216
217    error = ps_fdt_cleanup_cookie(&io_ops->malloc_ops, cookie);
218    if (error) {
219        return -ENODEV;
220    }
221
222
223    /* Get the base addr of the doorbell
224     * Section 14.8.5: All doorbell registers are in a single page, doorbell
225     * {db} has a register range starting at DB{db}_BASE = HSP_{inst}_BASE +
226     * (1+ nSM/2 + nSS + nAS) * 64 KiB + {db} * 0x100. */
227
228    int num_sm = 0, num_ss = 0, num_as = 0;
229
230    uint32_t *int_dim_reg = hsp_priv->hsp_base + HSP_INT_DIMENSION_OFFSET;
231
232    num_sm = (*int_dim_reg >> HSP_INT_DIMENSION_SM_SHIFT) & HSP_INT_DIMENSION_NUM_MASK;
233    num_ss = (*int_dim_reg >> HSP_INT_DIMENSION_SS_SHIFT) & HSP_INT_DIMENSION_NUM_MASK;
234    num_as = (*int_dim_reg >> HSP_INT_DIMENSION_AS_SHIFT) & HSP_INT_DIMENSION_NUM_MASK;
235
236    hsp_priv->doorbell_base = hsp_priv->hsp_base + (1 + (num_sm / 2) + num_ss + num_as) * 0x10000;
237
238    hsp->data = hsp_priv;
239    hsp->ring = hsp_doorbell_ring;
240    hsp->check = hsp_doorbell_check;
241    hsp->destroy = hsp_destroy;
242
243    return 0;
244}
245