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