1/*
2 * Copyright 2016, General Dynamics C4 Systems
3 *
4 * SPDX-License-Identifier: GPL-2.0-only
5 */
6
7#include <types.h>
8#include <config.h>
9
10#ifdef CONFIG_TK1_SMMU
11
12#include <plat/machine/smmu.h>
13#include <linker.h>
14#include <plat/machine/hardware.h>
15#include <object/structures.h>
16
17
18#define MC_PADDR                    0x70019000
19#define SMMU_CONFIG_OFFSET          0x10
20
21#define PTB_DATA_BASE_SHIFT         12
22#define PTB_DATA_READ               BIT(31)
23#define PTB_DATA_WRITE              BIT(30)
24#define PTB_DATA_NONSECURE          BIT(29)
25#define PTB_DATA_BASE_PD_MASK       0x3fffff
26
27#define MODULE_ASID_ENABLE          BIT(31)
28
29#define PTC_FLUSH_ALL               0
30#define PTC_FLUSH_ADR               1
31
32#define TLB_ASID_MATCH              BIT(31)
33#define TLB_FLUSH_ALL               (0)
34#define TLB_FLUSH_SECTION           (2)
35#define TLB_FLUSH_GROUP             (3)
36
37#define MC_DECERR_MTS_BIT           16u
38#define MC_SECERR_SEC_BIT           13u
39#define MC_DECERR_VPR_BIT           12u
40#define MC_APB_ASID_UPDATE_BIT      11u
41#define MC_SMMU_PAGE_BIT            10u
42#define MC_ARBITRATION_EMEM_BIT     9u
43#define MC_SECURITY_BIT             8u
44#define MC_DECERR_EMEM_BIT          6u
45
46
47#define MC_ERR_ID_MASK              0x7f
48#define MC_ERR_ADR_MASK             0x7000
49#define MC_ERR_RW_MASK              0x10000
50#define MC_ERR_SEC_MASK             0x20000
51#define MC_ERR_SWAP_MASK            0x40000
52#define MC_ERR_ADR_HI_MASK          0x300000
53#define MC_ERR_INVALID_SMMU_PAGE_NONSECURE_MASK     0x2000000
54#define MC_ERR_INVALID_SMMU_PAGE_WRITE_MASK         0x4000000
55#define MC_ERR_INVALID_SMMU_PAGE_READ_MASK          0x8000000
56#define MC_ERR_TYPE_MASK                            0x70000000
57#define MC_ERR_TYPE_SHIFT                           28
58
59#define MC_ERR_TYPE_RSVD                0
60#define MC_ERR_TYPE_DECERR_EMEM         2
61#define MC_ERR_TYPE_SECURITY            3
62#define MC_ERR_TYPE_SECURITY_CARVEOUT   4
63#define MC_ERR_TYPE_INVALID_SMMU_PAGE   6
64
65#define IOPDE_4M_INDEX_SHIFT            22
66
67static volatile tk1_mc_regs_t *smmu_regs = (volatile tk1_mc_regs_t *)(SMMU_PPTR);
68
69static char smmu_pds[ARM_PLAT_NUM_SMMU][BIT(SMMU_PD_INDEX_BITS)] ALIGN(BIT(SMMU_PD_INDEX_BITS));
70
71static void do_smmu_enable(void)
72{
73    volatile uint32_t *config = (volatile uint32_t *)(MC_PADDR + SMMU_CONFIG_OFFSET);
74    *config = 1;
75}
76
77static void do_smmu_disable(void)
78{
79    volatile uint32_t *config = (volatile uint32_t *)(MC_PADDR + SMMU_CONFIG_OFFSET);
80    *config = 0;
81}
82
83static inline void smmu_disable(void)
84{
85    if (config_set(CONFIG_ARM_HYPERVISOR_SUPPORT)) {
86        /* in hyp mode, we need call the hook in monitor mode */
87        /* we need physical address here */
88        paddr_t addr = addrFromPPtr(&do_smmu_disable);
89        asm(".arch_extension sec\n");
90        asm volatile("mov r0, %0\n\t"
91                     "dsb\nisb\n"
92                     "smc #0\n"
93                     ::"r"(addr):"r0", "r1", "r2", "r3", "ip");
94    } else {
95        /* in secure mode, can enable it directly */
96        smmu_regs->smmu_config = 0;
97    }
98
99    return;
100}
101
102static inline void smmu_enable(void)
103{
104    if (config_set(CONFIG_ARM_HYPERVISOR_SUPPORT)) {
105        paddr_t addr = addrFromPPtr(&do_smmu_enable);
106        asm(".arch_extension sec\n");
107        asm volatile("mov r0, %0\n\t"
108                     "dsb\nisb\n"
109                     "smc #0\n"
110                     ::"r"(addr):"r0", "r1", "r2", "r3", "ip");
111    } else {
112        smmu_regs->smmu_config = 1;
113    }
114
115    return;
116}
117
118
119static uint32_t make_ptb_data(uint32_t pd_base, bool_t read, bool_t write, bool_t nonsecure)
120{
121    uint32_t ret = 0;
122    ret = (pd_base >> PTB_DATA_BASE_SHIFT);
123
124    if (read) {
125        ret |= PTB_DATA_READ;
126    }
127    if (write) {
128        ret |= PTB_DATA_WRITE;
129    }
130    if (nonsecure) {
131        ret |= PTB_DATA_NONSECURE;
132    }
133
134    return ret;
135}
136
137void plat_smmu_ptc_flush_all(void)
138{
139    uint32_t cmd = PTC_FLUSH_ALL;
140    smmu_regs->smmu_ptc_flush = cmd;
141}
142
143void plat_smmu_tlb_flush_all(void)
144{
145    uint32_t cmd = TLB_FLUSH_ALL;
146    smmu_regs->smmu_tlb_flush = cmd;
147}
148
149BOOT_CODE int plat_smmu_init(void)
150{
151    uint32_t asid;
152
153    smmu_disable();
154
155    for (asid = SMMU_FIRST_ASID; asid <= SMMU_LAST_ASID; asid++) {
156        iopde_t *pd = (iopde_t *) smmu_pds[asid - SMMU_FIRST_ASID];
157
158        memset(pd, 0, BIT(SMMU_PD_INDEX_BITS));
159        cleanCacheRange_RAM((word_t)pd, ((word_t)pd + BIT(SMMU_PD_INDEX_BITS)),
160                            addrFromPPtr(pd));
161
162        smmu_regs->smmu_ptb_asid = asid;
163
164        /* make it read/write/nonsecure but all translation entries are invalid */
165        smmu_regs->smmu_ptb_data = make_ptb_data(pptr_to_paddr(pd), true, true, true);
166    }
167    printf("Total %d IOASID set up\n", (asid - 1));
168
169    /* now assign IOASID to each module */
170    smmu_regs->smmu_afi_asid = SMMU_AFI_ASID | MODULE_ASID_ENABLE;
171    smmu_regs->smmu_avpc_asid = SMMU_AVPC_ASID | MODULE_ASID_ENABLE;
172    smmu_regs->smmu_dc_asid = SMMU_DC_ASID | MODULE_ASID_ENABLE;
173    smmu_regs->smmu_dcb_asid = SMMU_DCB_ASID | MODULE_ASID_ENABLE;
174    smmu_regs->smmu_hc_asid = SMMU_HC_ASID | MODULE_ASID_ENABLE;
175    smmu_regs->smmu_hda_asid = SMMU_HDA_ASID | MODULE_ASID_ENABLE;
176    smmu_regs->smmu_isp2_asid = SMMU_ISP2_ASID | MODULE_ASID_ENABLE;
177    smmu_regs->smmu_msenc_asid = SMMU_MSENC_ASID | MODULE_ASID_ENABLE;
178    smmu_regs->smmu_nv_asid = SMMU_NV_ASID | MODULE_ASID_ENABLE;
179    smmu_regs->smmu_nv2_asid = SMMU_NV2_ASID | MODULE_ASID_ENABLE;
180    smmu_regs->smmu_ppcs_asid = SMMU_PPCS_ASID | MODULE_ASID_ENABLE;
181    smmu_regs->smmu_sata_asid = SMMU_SATA_ASID | MODULE_ASID_ENABLE;
182    smmu_regs->smmu_vde_asid = SMMU_VDE_ASID | MODULE_ASID_ENABLE;
183    smmu_regs->smmu_vi_asid = SMMU_VI_ASID | MODULE_ASID_ENABLE;
184    smmu_regs->smmu_vic_asid = SMMU_VIC_ASID | MODULE_ASID_ENABLE;
185    smmu_regs->smmu_xusb_host_asid = SMMU_XUSB_HOST_ASID | MODULE_ASID_ENABLE;
186    smmu_regs->smmu_xusb_dev_asid = SMMU_XUSB_DEV_ASID | MODULE_ASID_ENABLE;
187    smmu_regs->smmu_tsec_asid = SMMU_TSEC_ASID | MODULE_ASID_ENABLE;
188    smmu_regs->smmu_ppcs1_asid = SMMU_PPCS1_ASID | MODULE_ASID_ENABLE;
189    smmu_regs->smmu_sdmmc1a_asid = SMMU_SDMMC1A_ASID | MODULE_ASID_ENABLE;
190    smmu_regs->smmu_sdmmc2a_asid = SMMU_SDMMC2A_ASID | MODULE_ASID_ENABLE;
191    smmu_regs->smmu_sdmmc3a_asid = SMMU_SDMMC3A_ASID | MODULE_ASID_ENABLE;
192    smmu_regs->smmu_sdmmc4a_asid = SMMU_SDMMC4A_ASID | MODULE_ASID_ENABLE;
193    smmu_regs->smmu_isp2b_asid = SMMU_ISP2B_ASID | MODULE_ASID_ENABLE;
194    smmu_regs->smmu_gpu_asid = SMMU_GPU_ASID | MODULE_ASID_ENABLE;
195    smmu_regs->smmu_gpub_asid = SMMU_GPUB_ASID | MODULE_ASID_ENABLE;
196    smmu_regs->smmu_ppcs2_asid = SMMU_PPCS2_ASID | MODULE_ASID_ENABLE;
197
198    /* flush page table cache */
199    plat_smmu_ptc_flush_all();
200    /* flush TLB              */
201    plat_smmu_tlb_flush_all();
202    smmu_enable();
203
204    /* also need to unmask interrupts */
205    if (config_set(CONFIG_SMMU_INTERRUPT_ENABLE)) {
206        smmu_regs->intmask = BIT(MC_APB_ASID_UPDATE_BIT) | BIT(MC_SMMU_PAGE_BIT) |
207                             BIT(MC_DECERR_MTS_BIT) | BIT(MC_SECERR_SEC_BIT) |
208                             BIT(MC_DECERR_VPR_BIT) | BIT(MC_ARBITRATION_EMEM_BIT) |
209                             BIT(MC_SECURITY_BIT) | BIT(MC_DECERR_EMEM_BIT);
210    } else {
211        smmu_regs->intmask = 0;
212    }
213    return ARM_PLAT_NUM_SMMU;
214}
215
216iopde_t *plat_smmu_lookup_iopd_by_asid(uint32_t asid)
217{
218    /* There should be no way to generate bad ASID values through the kernel
219     * so this is an assertion and not a check */
220    assert(asid >= SMMU_FIRST_ASID && asid <= SMMU_LAST_ASID);
221    return (iopde_t *) smmu_pds[asid - SMMU_FIRST_ASID];
222}
223
224void plat_smmu_handle_interrupt(void)
225{
226    uint32_t status = smmu_regs->intstatus;
227    uint32_t clear_status = 0;
228
229    if (status & BIT(MC_DECERR_MTS_BIT)) {
230        clear_status |= BIT(MC_DECERR_MTS_BIT);
231    }
232    if (status & BIT(MC_SECERR_SEC_BIT)) {
233        clear_status |= BIT(MC_SECERR_SEC_BIT);
234    }
235    if (status & BIT(MC_DECERR_VPR_BIT)) {
236        clear_status |= BIT(MC_DECERR_VPR_BIT);
237    }
238    if (status & BIT(MC_ARBITRATION_EMEM_BIT)) {
239        clear_status |= BIT(MC_ARBITRATION_EMEM_BIT);
240    }
241    if (status & BIT(MC_SECURITY_BIT)) {
242        clear_status |= BIT(MC_SECURITY_BIT);
243    }
244    if (status & BIT(MC_DECERR_EMEM_BIT)) {
245        clear_status |= BIT(MC_DECERR_EMEM_BIT);
246    }
247    if (status & BIT(MC_APB_ASID_UPDATE_BIT)) {
248        clear_status |= BIT(MC_APB_ASID_UPDATE_BIT);
249    }
250
251    /* we only care about SMMU translation failures */
252    if (status & BIT(MC_SMMU_PAGE_BIT)) {
253        if (config_set(CONFIG_PRINTING)) {
254            uint32_t err_status = smmu_regs->err_status;
255            uint32_t UNUSED err_adr = smmu_regs->err_adr;
256            uint32_t UNUSED id = err_status & MC_ERR_ID_MASK;
257            uint32_t UNUSED rw = (err_status & MC_ERR_RW_MASK);
258            uint32_t UNUSED read = (err_status & MC_ERR_INVALID_SMMU_PAGE_READ_MASK);
259            uint32_t UNUSED write = (err_status & MC_ERR_INVALID_SMMU_PAGE_WRITE_MASK);
260            uint32_t UNUSED nonsecure = (err_status & MC_ERR_INVALID_SMMU_PAGE_NONSECURE_MASK);
261            uint32_t UNUSED type = (err_status & MC_ERR_TYPE_MASK) >> MC_ERR_TYPE_SHIFT;
262
263            printf("SMMU Address translation error:\n");
264            printf("ID: %d address: 0x%x type: %d direction: 0x%x\n", id, err_adr, type, rw);
265            printf("IOPT permission: read 0x%x write 0x%x nonsecure 0x%x\n", read, write, nonsecure);
266        }
267        clear_status |= BIT(MC_SMMU_PAGE_BIT);
268    }
269
270    /* write 1 to clear the interrupt */
271    smmu_regs->intstatus = clear_status;
272}
273#endif
274