1 /*
2  * SPDX-FileCopyrightText: Copyright (c) 2018-2020 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3  * SPDX-License-Identifier: MIT
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in
13  * all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21  * DEALINGS IN THE SOFTWARE.
22  */
23 
24 #include "common_nvswitch.h"
25 #include "lr10/lr10.h"
26 #include "flcn/flcn_nvswitch.h"
27 
28 #include "nvswitch/lr10/dev_falcon_v4.h"
29 
30 static NvU32
_flcnRegRead_LR10(nvswitch_device * device,PFLCN pFlcn,NvU32 offset)31 _flcnRegRead_LR10
32 (
33     nvswitch_device *device,
34     PFLCN            pFlcn,
35     NvU32            offset
36 )
37 {
38     // Probably should perform some checks on the offset, the device, and the engine descriptor
39     return nvswitch_reg_read_32(device, pFlcn->engDescUc.base + offset);
40 }
41 
42 static void
_flcnRegWrite_LR10(nvswitch_device * device,PFLCN pFlcn,NvU32 offset,NvU32 data)43 _flcnRegWrite_LR10
44 (
45     nvswitch_device    *device,
46     PFLCN               pFlcn,
47     NvU32               offset,
48     NvU32               data
49 )
50 {
51     // Probably should perform some checks on the offset, the device, and the engine descriptor
52     nvswitch_reg_write_32(device, pFlcn->engDescUc.base + offset, data);
53 }
54 
55 /*
56  * @brief Retrigger an interrupt message from the engine to the NV_CTRL tree
57  *
58  * @param[in] device  nvswitch_device pointer
59  * @param[in] pFlcn   FLCN pointer
60  */
61 static void
_flcnIntrRetrigger_LR10(nvswitch_device * device,FLCN * pFlcn)62 _flcnIntrRetrigger_LR10
63 (
64     nvswitch_device    *device,
65     FLCN               *pFlcn
66 )
67 {
68     NvU32 val = DRF_DEF(_PFALCON, _FALCON_INTR_RETRIGGER, _TRIGGER, _TRUE);
69     flcnRegWrite_HAL(device, pFlcn, NV_PFALCON_FALCON_INTR_RETRIGGER(0), val);
70 }
71 
72 static NvBool
_flcnAreEngDescsInitialized_LR10(nvswitch_device * device,FLCN * pFlcn)73 _flcnAreEngDescsInitialized_LR10
74 (
75     nvswitch_device    *device,
76     FLCN               *pFlcn
77 )
78 {
79     // if pFlcn->engDescUc is 0, we haven't finished discovery, return false
80     // if pFlcn->engDescUc is NOT 0, and pFlcn->engDescBc is NULL, this is a unicast only engine
81     return   pFlcn->engDescUc.base != 0 && pFlcn->engDescUc.initialized &&
82             (pFlcn->engDescBc.base == 0 || pFlcn->engDescBc.initialized);
83 }
84 
85 /*
86  *  @brief Waits for falcon to finish scrubbing IMEM/DMEM.
87  *
88  *  @param[in] device   switch device
89  *  @param[in] pFlcn    FLCN pointer
90  *
91  *  @returns nothing
92  */
93 static NV_STATUS
_flcnWaitForResetToFinish_LR10(nvswitch_device * device,PFLCN pFlcn)94 _flcnWaitForResetToFinish_LR10
95 (
96     nvswitch_device    *device,
97     PFLCN               pFlcn
98 )
99 {
100     NVSWITCH_TIMEOUT timeout;
101     NvU32 dmaCtrl;
102 
103     // Add a dummy write (of anything) to trigger scrubbing
104     flcnRegWrite_HAL(device, pFlcn, NV_PFALCON_FALCON_MAILBOX0, 0);
105 
106     // TODO: Adapt timeout to our model, this should be centralized.
107     if (IS_EMULATION(device))
108     {
109         nvswitch_timeout_create(NVSWITCH_INTERVAL_1SEC_IN_NS, &timeout);
110     }
111     else
112     {
113         nvswitch_timeout_create(NVSWITCH_INTERVAL_5MSEC_IN_NS, &timeout);
114     }
115 
116     while (1)
117     {
118         dmaCtrl = flcnRegRead_HAL(device, pFlcn, NV_PFALCON_FALCON_DMACTL);
119 
120         if (FLD_TEST_DRF(_PFALCON, _FALCON_DMACTL, _DMEM_SCRUBBING, _DONE, dmaCtrl) &&
121             FLD_TEST_DRF(_PFALCON, _FALCON_DMACTL, _IMEM_SCRUBBING, _DONE, dmaCtrl))
122         {
123             // Operation successful, IMEM and DMEM scrubbing has finished.
124             return NV_OK;
125         }
126 
127         if (nvswitch_timeout_check(&timeout))
128         {
129             NVSWITCH_PRINT(device, ERROR,
130                 "%s: Timeout waiting for scrubbing to finish!!!\n",
131                 __FUNCTION__);
132             NVSWITCH_ASSERT(0);
133             return NV_ERR_TIMEOUT;
134         }
135     }
136 }
137 
138 /*!
139  * @brief   Capture and dump the falconPC trace.
140  *
141  * @param[in]  device     nvswitch device pointer
142  * @param[in]  pFlcn      FLCN object pointer
143  *
144  * @returns nothing
145  */
146 void
_flcnDbgInfoCapturePcTrace_LR10(nvswitch_device * device,PFLCN pFlcn)147 _flcnDbgInfoCapturePcTrace_LR10
148 (
149     nvswitch_device *device,
150     PFLCN            pFlcn
151 )
152 {
153     NvU32    regTraceIdx;
154     NvU32    idx;
155     NvU32    maxIdx;
156 
157     // Dump entire PC trace buffer
158     regTraceIdx = flcnRegRead_HAL(device, pFlcn, NV_PFALCON_FALCON_TRACEIDX);
159     maxIdx      = DRF_VAL(_PFALCON_FALCON, _TRACEIDX, _MAXIDX, regTraceIdx);
160 
161     NVSWITCH_PRINT(device, ERROR,
162               "PC TRACE (TOTAL %d ENTRIES. Entry 0 is the most recent branch):\n",
163               maxIdx);
164 
165     for (idx = 0; idx < maxIdx; idx++)
166     {
167         regTraceIdx =
168             FLD_SET_DRF_NUM(_PFALCON, _FALCON_TRACEIDX, _IDX, idx, regTraceIdx);
169 
170         flcnRegWrite_HAL(device, pFlcn, NV_PFALCON_FALCON_TRACEIDX, regTraceIdx);
171 
172         NVSWITCH_PRINT(device, ERROR, "FALCON_TRACEPC(%d)     : 0x%08x\n", idx,
173             DRF_VAL(_PFALCON, _FALCON_TRACEPC, _PC,
174                 flcnRegRead_HAL(device, pFlcn, NV_PFALCON_FALCON_TRACEPC)));
175     }
176 }
177 
178 /*!
179  * @brief Read falcon core revision
180  *
181  * @param[in] device nvswitch_device pointer
182  * @param[in] pFlcn  FLCN pointer
183  *
184  * @return @ref NV_FLCN_CORE_REV_X_Y.
185  */
186 NvU8
_flcnReadCoreRev_LR10(nvswitch_device * device,PFLCN pFlcn)187 _flcnReadCoreRev_LR10
188 (
189     nvswitch_device *device,
190     PFLCN            pFlcn
191 )
192 {
193     NvU32 hwcfg1 = flcnRegRead_HAL(device, pFlcn, NV_PFALCON_FALCON_HWCFG1);
194 
195     return ((DRF_VAL(_PFALCON, _FALCON_HWCFG1, _CORE_REV, hwcfg1) << 4) |
196             DRF_VAL(_PFALCON, _FALCON_HWCFG1, _CORE_REV_SUBVERSION, hwcfg1));
197 }
198 
199 //
200 // Store pointers to ucode header and data.
201 // Preload ucode from registry if available.
202 //
203 NV_STATUS
_flcnConstruct_LR10(nvswitch_device * device,PFLCN pFlcn)204 _flcnConstruct_LR10
205 (
206     nvswitch_device    *device,
207     PFLCN               pFlcn
208 )
209 {
210     NV_STATUS          status;
211     PFLCNABLE          pFlcnable = pFlcn->pFlcnable;
212     PFALCON_QUEUE_INFO pQueueInfo;
213     pFlcn->bConstructed         = NV_TRUE;
214 
215     // Set the arch to Falcon
216     pFlcn->engArch = NV_UPROC_ENGINE_ARCH_FALCON;
217 
218     // Allocate the memory for Queue Data Structure if needed.
219     if (pFlcn->bQueuesEnabled)
220     {
221         pQueueInfo = pFlcn->pQueueInfo = nvswitch_os_malloc(sizeof(*pQueueInfo));
222         if (pQueueInfo == NULL)
223         {
224             status = NV_ERR_NO_MEMORY;
225             NVSWITCH_ASSERT(0);
226             goto _flcnConstruct_LR10_fail;
227         }
228         nvswitch_os_memset(pQueueInfo, 0, sizeof(FALCON_QUEUE_INFO));
229         // Assert if Number of Queues are zero
230         NVSWITCH_ASSERT(pFlcn->numQueues != 0);
231         pQueueInfo->pQueues = nvswitch_os_malloc(sizeof(FLCNQUEUE) * pFlcn->numQueues);
232         if (pQueueInfo->pQueues == NULL)
233         {
234             status = NV_ERR_NO_MEMORY;
235             NVSWITCH_ASSERT(0);
236             goto _flcnConstruct_LR10_fail;
237         }
238         nvswitch_os_memset(pQueueInfo->pQueues, 0, sizeof(FLCNQUEUE) * pFlcn->numQueues);
239         // Sequences can be optional
240         if (pFlcn->numSequences != 0)
241         {
242             if ((pFlcn->numSequences - 1) > ((NvU32)NV_U8_MAX))
243             {
244                 status = NV_ERR_OUT_OF_RANGE;
245                 NVSWITCH_PRINT(device, ERROR,
246                           "Max numSequences index = %d cannot fit into byte\n",
247                           (pFlcn->numSequences - 1));
248                 NVSWITCH_ASSERT(0);
249                 goto _flcnConstruct_LR10_fail;
250             }
251             flcnQueueSeqInfoStateInit(device, pFlcn);
252         }
253     }
254     // DEBUG
255     NVSWITCH_PRINT(device, INFO, "Falcon: %s\n", flcnGetName_HAL(device, pFlcn));
256     NVSWITCH_ASSERT(pFlcnable != NULL);
257     flcnableGetExternalConfig(device, pFlcnable, &pFlcn->extConfig);
258     return NV_OK;
259 _flcnConstruct_LR10_fail:
260     // call flcnDestruct to free the memory allocated in this construct function
261     flcnDestruct_HAL(device, pFlcn);
262     return status;
263 }
264 
265 void
_flcnDestruct_LR10(nvswitch_device * device,PFLCN pFlcn)266 _flcnDestruct_LR10
267 (
268     nvswitch_device    *device,
269     PFLCN               pFlcn
270 )
271 {
272     PFALCON_QUEUE_INFO pQueueInfo;
273     PFLCNABLE pFlcnable = pFlcn->pFlcnable;
274     if (!pFlcn->bConstructed)
275     {
276         return;
277     }
278     pFlcn->bConstructed = NV_FALSE;
279     if (pFlcnable == NULL) {
280         NVSWITCH_ASSERT(pFlcnable != NULL);
281         return;
282     }
283     if (pFlcn->bQueuesEnabled && (pFlcn->pQueueInfo != NULL))
284     {
285         pQueueInfo = pFlcn->pQueueInfo;
286         if (NULL != pQueueInfo->pQueues)
287         {
288             nvswitch_os_free(pQueueInfo->pQueues);
289             pQueueInfo->pQueues = NULL;
290         }
291         nvswitch_os_free(pFlcn->pQueueInfo);
292         pFlcn->pQueueInfo = NULL;
293     }
294 }
295 const char *
_flcnGetName_LR10(nvswitch_device * device,PFLCN pFlcn)296 _flcnGetName_LR10
297 (
298     nvswitch_device    *device,
299     PFLCN               pFlcn
300 )
301 {
302     if (pFlcn->name == NULL)
303     {
304         return "UNKNOWN";
305     }
306     return pFlcn->name;
307 }
308 
309 /**
310  * @brief   set hal function pointers for functions defined in LR10 (i.e. this file)
311  *
312  * this function has to be at the end of the file so that all the
313  * other functions are already defined.
314  *
315  * @param[in] pFlcn   The flcn for which to set hals
316  */
317 void
flcnSetupHal_LR10(PFLCN pFlcn)318 flcnSetupHal_LR10
319 (
320     PFLCN            pFlcn
321 )
322 {
323     flcn_hal *pHal = pFlcn->pHal;
324 
325     pHal->readCoreRev              = _flcnReadCoreRev_LR10;
326     pHal->regRead                  = _flcnRegRead_LR10;
327     pHal->regWrite                 = _flcnRegWrite_LR10;
328     pHal->construct                = _flcnConstruct_LR10;
329     pHal->destruct                 = _flcnDestruct_LR10;
330     pHal->getName                  = _flcnGetName_LR10;
331     pHal->intrRetrigger            = _flcnIntrRetrigger_LR10;
332     pHal->areEngDescsInitialized   = _flcnAreEngDescsInitialized_LR10;
333     pHal->waitForResetToFinish     = _flcnWaitForResetToFinish_LR10;
334     pHal->dbgInfoCapturePcTrace    = _flcnDbgInfoCapturePcTrace_LR10;
335 }
336