xref: /aosp_15_r20/external/coreboot/src/vendorcode/cavium/bdk/libbdk-hal/bdk-ecam-io.c (revision b9411a12aaaa7e1e6a6fb7c5e057f44ee179a49c)
1 /***********************license start***********************************
2 * Copyright (c) 2003-2017  Cavium Inc. ([email protected]). All rights
3 * reserved.
4 *
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
8 * met:
9 *
10 *   * Redistributions of source code must retain the above copyright
11 *     notice, this list of conditions and the following disclaimer.
12 *
13 *   * Redistributions in binary form must reproduce the above
14 *     copyright notice, this list of conditions and the following
15 *     disclaimer in the documentation and/or other materials provided
16 *     with the distribution.
17 *
18 *   * Neither the name of Cavium Inc. nor the names of
19 *     its contributors may be used to endorse or promote products
20 *     derived from this software without specific prior written
21 *     permission.
22 *
23 * This Software, including technical data, may be subject to U.S. export
24 * control laws, including the U.S. Export Administration Act and its
25 * associated regulations, and may be subject to export or import
26 * regulations in other countries.
27 *
28 * TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
29 * AND WITH ALL FAULTS AND CAVIUM INC. MAKES NO PROMISES, REPRESENTATIONS OR
30 * WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT
31 * TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY
32 * REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT
33 * DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES
34 * OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR
35 * PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT,
36 * QUIET POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE  RISK
37 * ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
38 ***********************license end**************************************/
39 #include <bdk.h>
40 #include "libbdk-arch/bdk-csrs-ecam.h"
41 #include "libbdk-arch/bdk-csrs-gser.h"
42 #include "libbdk-arch/bdk-csrs-pccpf.h"
43 #include "libbdk-arch/bdk-csrs-pem.h"
44 #include "libbdk-hal/device/bdk-device.h"
45 #include "libbdk-hal/bdk-ecam.h"
46 
47 #if 1 /* Support CN88XX pass 1.0 */
48 /*******************************************************************
49  *******************************************************************
50     These functions are related to CN88XX pass 1.0 errata and do not
51     apply to any other chip
52  *******************************************************************
53  *******************************************************************/
54 
55 /**
56  * Errata (ECAM-22630) ECAM function accesses can fault
57  * For some errata workaround we need a check to tell if a ECAM access is to a
58  * valid intenral device. This function decodes a pcc_dev_con_e enumeration and
59  * checks if the supplied arguments match it. This should only
60  * ever be called on CN88XX pass 1.0.
61  *
62  * @param ecam    ECAM to check
63  * @param bus     ECAM bus number
64  * @param dev     Device to check
65  * @param fn      sub function of device
66  * @param dev_con Enumeration to match against
67  *
68  * @return Non zero if the device matches
69  */
is_internal_cn88xxp1_0(const bdk_device_t * device,int dev_con)70 static int is_internal_cn88xxp1_0(const bdk_device_t *device, int dev_con)
71 {
72     union bdk_pcc_dev_con_s d = { .u = dev_con };
73     return (d.cn8.ecam == device->ecam) && (d.s.bus == device->bus) && (d.s.func == ((device->dev<<3)|device->func));
74 }
75 
76 /**
77  * Errata (ECAM-22630) ECAM function accesses can fault
78  * This is a companion to the function above to determine if the ECAM device is
79  * any of the valid internal devices. This should only ever be
80  * called on CN88XX pass 1.0.
81  *
82  * @param ecam   ECAM to check
83  * @param bus    ECAM bus number
84  * @param dev    Device to check
85  * @param fn     sub function of device
86  *
87  * @return Non zero if the device matches
88  */
is_any_internal_cn88xxp1_0(const bdk_device_t * device)89 static int is_any_internal_cn88xxp1_0(const bdk_device_t *device)
90 {
91     /* Errata (ECAM-22630) ECAM function accesses can fault
92         CN88XXP1.0: The ECAM has a bug where accessing a non-existent
93         device causes an exception. This is a list of all valid devices
94         for CN88XX pass 1.0 */
95     static const uint32_t INTERNAL_DEVICES_CN88XXP1_0[] = {
96         BDK_PCC_DEV_CON_E_BGXX(0),
97         BDK_PCC_DEV_CON_E_BGXX(1),
98         BDK_PCC_DEV_CON_E_DAP,
99         BDK_PCC_DEV_CON_E_DFA,
100         BDK_PCC_DEV_CON_E_FUSF,
101         BDK_PCC_DEV_CON_E_GIC_CN8,
102         BDK_PCC_DEV_CON_E_GPIO_CN8,
103         BDK_PCC_DEV_CON_E_GSERX(0),
104         BDK_PCC_DEV_CON_E_GSERX(1),
105         BDK_PCC_DEV_CON_E_GSERX(10),
106         BDK_PCC_DEV_CON_E_GSERX(11),
107         BDK_PCC_DEV_CON_E_GSERX(12),
108         BDK_PCC_DEV_CON_E_GSERX(13),
109         BDK_PCC_DEV_CON_E_GSERX(2),
110         BDK_PCC_DEV_CON_E_GSERX(3),
111         BDK_PCC_DEV_CON_E_GSERX(4),
112         BDK_PCC_DEV_CON_E_GSERX(5),
113         BDK_PCC_DEV_CON_E_GSERX(6),
114         BDK_PCC_DEV_CON_E_GSERX(7),
115         BDK_PCC_DEV_CON_E_GSERX(8),
116         BDK_PCC_DEV_CON_E_GSERX(9),
117         BDK_PCC_DEV_CON_E_GTI_CN8,
118         BDK_PCC_DEV_CON_E_IOBNX(0),
119         BDK_PCC_DEV_CON_E_IOBNX(1),
120         BDK_PCC_DEV_CON_E_KEY,
121         BDK_PCC_DEV_CON_E_L2C,
122         BDK_PCC_DEV_CON_E_L2C_CBCX(0),
123         BDK_PCC_DEV_CON_E_L2C_CBCX(1),
124         BDK_PCC_DEV_CON_E_L2C_CBCX(2),
125         BDK_PCC_DEV_CON_E_L2C_CBCX(3),
126         BDK_PCC_DEV_CON_E_L2C_MCIX(0),
127         BDK_PCC_DEV_CON_E_L2C_MCIX(1),
128         BDK_PCC_DEV_CON_E_L2C_MCIX(2),
129         BDK_PCC_DEV_CON_E_L2C_MCIX(3),
130         BDK_PCC_DEV_CON_E_L2C_TADX(0),
131         BDK_PCC_DEV_CON_E_L2C_TADX(1),
132         BDK_PCC_DEV_CON_E_L2C_TADX(2),
133         BDK_PCC_DEV_CON_E_L2C_TADX(3),
134         BDK_PCC_DEV_CON_E_L2C_TADX(4),
135         BDK_PCC_DEV_CON_E_L2C_TADX(5),
136         BDK_PCC_DEV_CON_E_L2C_TADX(6),
137         BDK_PCC_DEV_CON_E_L2C_TADX(7),
138         BDK_PCC_DEV_CON_E_LMCX(0),
139         BDK_PCC_DEV_CON_E_LMCX(1),
140         BDK_PCC_DEV_CON_E_LMCX(2),
141         BDK_PCC_DEV_CON_E_LMCX(3),
142         BDK_PCC_DEV_CON_E_MIO_BOOT,
143         BDK_PCC_DEV_CON_E_MIO_EMM,
144         BDK_PCC_DEV_CON_E_MIO_FUS,
145         BDK_PCC_DEV_CON_E_MIO_PTP,
146         BDK_PCC_DEV_CON_E_MIO_TWSX(0),
147         BDK_PCC_DEV_CON_E_MIO_TWSX(1),
148         BDK_PCC_DEV_CON_E_MIO_TWSX(2),
149         BDK_PCC_DEV_CON_E_MIO_TWSX(3),
150         BDK_PCC_DEV_CON_E_MIO_TWSX(4),
151         BDK_PCC_DEV_CON_E_MIO_TWSX(5),
152         BDK_PCC_DEV_CON_E_MPI,
153         BDK_PCC_DEV_CON_E_MRML,
154         BDK_PCC_DEV_CON_E_NCSI,
155         BDK_PCC_DEV_CON_E_NIC_CN88XX,
156         BDK_PCC_DEV_CON_E_OCLAX_CN8(0),
157         BDK_PCC_DEV_CON_E_OCLAX_CN8(1),
158         BDK_PCC_DEV_CON_E_OCLAX_CN8(2),
159         BDK_PCC_DEV_CON_E_OCLAX_CN8(3),
160         BDK_PCC_DEV_CON_E_OCLAX_CN8(4),
161         BDK_PCC_DEV_CON_E_OCX,
162         BDK_PCC_DEV_CON_E_PCCBR_DFA,
163         BDK_PCC_DEV_CON_E_PCCBR_MRML,
164         BDK_PCC_DEV_CON_E_PCCBR_NIC_CN88XX,
165         BDK_PCC_DEV_CON_E_PCCBR_RAD_CN88XX,
166         BDK_PCC_DEV_CON_E_PCCBR_ZIP_CN88XX,
167         BDK_PCC_DEV_CON_E_PCIERC0_CN88XX,
168         BDK_PCC_DEV_CON_E_PCIERC1_CN88XX,
169         BDK_PCC_DEV_CON_E_PCIERC2_CN88XX,
170         BDK_PCC_DEV_CON_E_PCIERC3_CN88XX,
171         BDK_PCC_DEV_CON_E_PCIERC4,
172         BDK_PCC_DEV_CON_E_PCIERC5,
173         BDK_PCC_DEV_CON_E_PEMX(0),
174         BDK_PCC_DEV_CON_E_PEMX(1),
175         BDK_PCC_DEV_CON_E_PEMX(2),
176         BDK_PCC_DEV_CON_E_PEMX(3),
177         BDK_PCC_DEV_CON_E_PEMX(4),
178         BDK_PCC_DEV_CON_E_PEMX(5),
179         BDK_PCC_DEV_CON_E_RAD_CN88XX,
180         BDK_PCC_DEV_CON_E_RNM_CN88XX,
181         BDK_PCC_DEV_CON_E_RST,
182         BDK_PCC_DEV_CON_E_SATA0_CN88XX,
183         BDK_PCC_DEV_CON_E_SATA1_CN88XX,
184         BDK_PCC_DEV_CON_E_SATA10,
185         BDK_PCC_DEV_CON_E_SATA11,
186         BDK_PCC_DEV_CON_E_SATA12,
187         BDK_PCC_DEV_CON_E_SATA13,
188         BDK_PCC_DEV_CON_E_SATA14,
189         BDK_PCC_DEV_CON_E_SATA15,
190         BDK_PCC_DEV_CON_E_SATA2,
191         BDK_PCC_DEV_CON_E_SATA3,
192         BDK_PCC_DEV_CON_E_SATA4,
193         BDK_PCC_DEV_CON_E_SATA5,
194         BDK_PCC_DEV_CON_E_SATA6,
195         BDK_PCC_DEV_CON_E_SATA7,
196         BDK_PCC_DEV_CON_E_SATA8,
197         BDK_PCC_DEV_CON_E_SATA9,
198         BDK_PCC_DEV_CON_E_SGP,
199         BDK_PCC_DEV_CON_E_SLI0_CN88XX,
200         BDK_PCC_DEV_CON_E_SLI1,
201         BDK_PCC_DEV_CON_E_SMI,
202         BDK_PCC_DEV_CON_E_SMMU0_CN8,
203         BDK_PCC_DEV_CON_E_SMMU1,
204         BDK_PCC_DEV_CON_E_SMMU2,
205         BDK_PCC_DEV_CON_E_SMMU3,
206         BDK_PCC_DEV_CON_E_TNS,
207         BDK_PCC_DEV_CON_E_UAAX_CN8(0),
208         BDK_PCC_DEV_CON_E_UAAX_CN8(1),
209         BDK_PCC_DEV_CON_E_USBHX(0),
210         BDK_PCC_DEV_CON_E_USBHX(1),
211         BDK_PCC_DEV_CON_E_VRMX(0),
212         BDK_PCC_DEV_CON_E_VRMX(1),
213         BDK_PCC_DEV_CON_E_ZIP_CN88XX,
214         0,
215     };
216 
217     int loc = 0;
218     while (INTERNAL_DEVICES_CN88XXP1_0[loc])
219     {
220         if (is_internal_cn88xxp1_0(device, INTERNAL_DEVICES_CN88XXP1_0[loc]))
221             return 1;
222         loc++;
223     }
224     return 0;
225 }
226 
is_accessable_cn88xxp1_0(const bdk_device_t * device)227 static int is_accessable_cn88xxp1_0(const bdk_device_t *device)
228 {
229     /* Errata (ECAM-22630) ECAM function accesses can fault */
230     /* Skip internal devices that don't exists */
231     if (!is_any_internal_cn88xxp1_0(device))
232         return 0;
233 
234     /* Errata (ECAM-23020) PCIERC transactions fault unless PEM is
235        out of reset. The PCIe ports don't work until the PEM is
236        turned on. Check for one of the PCIe ports */
237     int pem = -1;
238     if (is_internal_cn88xxp1_0(device, BDK_PCC_DEV_CON_E_PCIERC0_CN88XX))
239         pem = 0;
240     if (is_internal_cn88xxp1_0(device, BDK_PCC_DEV_CON_E_PCIERC1_CN88XX))
241         pem = 1;
242     if (is_internal_cn88xxp1_0(device, BDK_PCC_DEV_CON_E_PCIERC2_CN88XX))
243         pem = 2;
244     if (is_internal_cn88xxp1_0(device, BDK_PCC_DEV_CON_E_PCIERC3_CN88XX))
245         pem = 3;
246     if (is_internal_cn88xxp1_0(device, BDK_PCC_DEV_CON_E_PCIERC4))
247         pem = 4;
248     if (is_internal_cn88xxp1_0(device, BDK_PCC_DEV_CON_E_PCIERC5))
249         pem = 5;
250     if (pem != -1)
251     {
252         BDK_CSR_INIT(pem_on, device->node, BDK_PEMX_ON(pem));
253         if (!pem_on.s.pemon || !pem_on.s.pemoor)
254             return 0;
255     }
256 
257     {
258         /* SATA ports should be hidden if they aren't configured at the QLM */
259         int qlm = -1;
260         if (is_internal_cn88xxp1_0(device, BDK_PCC_DEV_CON_E_SATA0_CN88XX) ||
261             is_internal_cn88xxp1_0(device, BDK_PCC_DEV_CON_E_SATA1_CN88XX) ||
262             is_internal_cn88xxp1_0(device, BDK_PCC_DEV_CON_E_SATA2) ||
263             is_internal_cn88xxp1_0(device, BDK_PCC_DEV_CON_E_SATA3))
264             qlm = 2;
265         if (is_internal_cn88xxp1_0(device, BDK_PCC_DEV_CON_E_SATA4) ||
266             is_internal_cn88xxp1_0(device, BDK_PCC_DEV_CON_E_SATA5) ||
267             is_internal_cn88xxp1_0(device, BDK_PCC_DEV_CON_E_SATA6) ||
268             is_internal_cn88xxp1_0(device, BDK_PCC_DEV_CON_E_SATA7))
269             qlm = 3;
270         if (is_internal_cn88xxp1_0(device, BDK_PCC_DEV_CON_E_SATA8) ||
271             is_internal_cn88xxp1_0(device, BDK_PCC_DEV_CON_E_SATA9) ||
272             is_internal_cn88xxp1_0(device, BDK_PCC_DEV_CON_E_SATA10) ||
273             is_internal_cn88xxp1_0(device, BDK_PCC_DEV_CON_E_SATA11))
274             qlm = 6;
275         if (is_internal_cn88xxp1_0(device, BDK_PCC_DEV_CON_E_SATA12) ||
276             is_internal_cn88xxp1_0(device, BDK_PCC_DEV_CON_E_SATA13) ||
277             is_internal_cn88xxp1_0(device, BDK_PCC_DEV_CON_E_SATA14) ||
278             is_internal_cn88xxp1_0(device, BDK_PCC_DEV_CON_E_SATA15))
279             qlm = 7;
280         if (qlm != -1)
281         {
282             BDK_CSR_INIT(cfg, device->node, BDK_GSERX_CFG(qlm));
283             if (!cfg.s.sata)
284                 return 0;
285         }
286     }
287     return 1;
288 }
289 
290 #endif /* Support CN88XX pass 1.0 */
291 
292 /**
293  * Build an ECAM config space request address for a device
294  *
295  * @param device Device being accessed
296  * @param reg    Register to access
297  *
298  * @return 64bit IO address
299  */
__bdk_ecam_build_address(const bdk_device_t * device,int reg)300 uint64_t __bdk_ecam_build_address(const bdk_device_t *device, int reg)
301 {
302     /* CN88XX pass 1.0 had a plethora of errata related to ECAM access. This
303        checks to make sure we're allowed to access this location based on
304        the various errata */
305     if (CAVIUM_IS_MODEL(CAVIUM_CN88XX_PASS1_0) && !is_accessable_cn88xxp1_0(device))
306         return 0;
307 
308     if (CAVIUM_IS_MODEL(CAVIUM_CN8XXX))
309     {
310         /* Build the address */
311         union bdk_ecam_cfg_addr_s address;
312         address.u = BDK_ECAM_BAR_E_ECAMX_PF_BAR2(device->ecam);
313         address.s.node = device->node;
314         address.s.bus = device->bus;
315         address.s.func = device->dev << 3 | device->func;
316         address.s.addr = reg;
317         return address.u;
318     }
319     else
320     {
321         /* Build the address. The architects decided to make it different
322            from CN8XXX for no obvious reason */
323         union bdk_ecam_cfg_addr_s address;
324         address.u = BDK_ECAM_BAR_E_ECAMX_PF_BAR2(0);
325         address.s.node = device->node;
326         address.s.dmn = device->ecam;
327         address.s.bus = device->bus;
328         address.s.func = device->dev << 3 | device->func;
329         address.s.addr = reg;
330         return address.u;
331     }
332 }
333 
334 /**
335  * Read from an ECAM
336  *
337  * @param device Device to read from
338  * @param reg    Register to read
339  *
340  * @return Result of the read of -1 on failure
341  */
bdk_ecam_read32(const bdk_device_t * device,int reg)342 uint32_t bdk_ecam_read32(const bdk_device_t *device, int reg)
343 {
344     uint64_t address = __bdk_ecam_build_address(device, reg);
345     uint32_t result;
346     if (address)
347         result = bdk_le32_to_cpu(bdk_read64_uint32(address));
348     else
349         result = 0xffffffff;
350 
351     /* Errata ECAM-22630: CN88XX pass 1.x, except pass 1.0, will return zero
352        for non-existent devices instead of ones. We look for this special case
353        for 32bit reads for reg=0 so we can scan device properly */
354     if (CAVIUM_IS_MODEL(CAVIUM_CN88XX_PASS1_X) && (reg == 0) && (result == 0))
355         result = 0xffffffff;
356 
357     return result;
358 }
359 
360 /**
361  * Write to an ECAM register
362  *
363  * @param device Device to write to
364  * @param reg    Register to write
365  * @param value  Value to write
366  */
bdk_ecam_write32(const bdk_device_t * device,int reg,uint32_t value)367 void bdk_ecam_write32(const bdk_device_t *device, int reg, uint32_t value)
368 {
369     uint64_t address = __bdk_ecam_build_address(device, reg);
370     if (address)
371         bdk_write64_uint32(address, bdk_cpu_to_le32(value));
372 }
373 
374