cvmx-pcie.c revision 210284
1210284Sjmallett/***********************license start***************
2210284Sjmallett *  Copyright (c) 2003-2008 Cavium Networks (support@cavium.com). All rights
3210284Sjmallett *  reserved.
4210284Sjmallett *
5210284Sjmallett *
6210284Sjmallett *  Redistribution and use in source and binary forms, with or without
7210284Sjmallett *  modification, are permitted provided that the following conditions are
8210284Sjmallett *  met:
9210284Sjmallett *
10210284Sjmallett *      * Redistributions of source code must retain the above copyright
11210284Sjmallett *        notice, this list of conditions and the following disclaimer.
12210284Sjmallett *
13210284Sjmallett *      * Redistributions in binary form must reproduce the above
14210284Sjmallett *        copyright notice, this list of conditions and the following
15210284Sjmallett *        disclaimer in the documentation and/or other materials provided
16210284Sjmallett *        with the distribution.
17210284Sjmallett *
18210284Sjmallett *      * Neither the name of Cavium Networks nor the names of
19210284Sjmallett *        its contributors may be used to endorse or promote products
20210284Sjmallett *        derived from this software without specific prior written
21210284Sjmallett *        permission.
22210284Sjmallett *
23210284Sjmallett *  TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
24210284Sjmallett *  AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS
25210284Sjmallett *  OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH
26210284Sjmallett *  RESPECT TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY
27210284Sjmallett *  REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT
28210284Sjmallett *  DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES
29210284Sjmallett *  OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR
30210284Sjmallett *  PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET
31210284Sjmallett *  POSSESSION OR CORRESPONDENCE TO DESCRIPTION.  THE ENTIRE RISK ARISING OUT
32210284Sjmallett *  OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
33210284Sjmallett *
34210284Sjmallett *
35210284Sjmallett *  For any questions regarding licensing please contact marketing@caviumnetworks.com
36210284Sjmallett *
37210284Sjmallett ***********************license end**************************************/
38210284Sjmallett
39210284Sjmallett
40210284Sjmallett
41210284Sjmallett
42210284Sjmallett
43210284Sjmallett
44210284Sjmallett/**
45210284Sjmallett * @file
46210284Sjmallett *
47210284Sjmallett * Interface to PCIe as a host(RC) or target(EP)
48210284Sjmallett *
49210284Sjmallett * <hr>$Revision: 41586 $<hr>
50210284Sjmallett */
51210284Sjmallett#include "cvmx.h"
52210284Sjmallett#include "cvmx-csr-db.h"
53210284Sjmallett#include "cvmx-pcie.h"
54210284Sjmallett#include "cvmx-sysinfo.h"
55210284Sjmallett#include "cvmx-swap.h"
56210284Sjmallett#include "cvmx-wqe.h"
57210284Sjmallett#include "cvmx-helper-errata.h"
58210284Sjmallett
59210284Sjmallett
60210284Sjmallett/**
61210284Sjmallett * Return the Core virtual base address for PCIe IO access. IOs are
62210284Sjmallett * read/written as an offset from this address.
63210284Sjmallett *
64210284Sjmallett * @param pcie_port PCIe port the IO is for
65210284Sjmallett *
66210284Sjmallett * @return 64bit Octeon IO base address for read/write
67210284Sjmallett */
68210284Sjmallettuint64_t cvmx_pcie_get_io_base_address(int pcie_port)
69210284Sjmallett{
70210284Sjmallett    cvmx_pcie_address_t pcie_addr;
71210284Sjmallett    pcie_addr.u64 = 0;
72210284Sjmallett    pcie_addr.io.upper = 0;
73210284Sjmallett    pcie_addr.io.io = 1;
74210284Sjmallett    pcie_addr.io.did = 3;
75210284Sjmallett    pcie_addr.io.subdid = 2;
76210284Sjmallett    pcie_addr.io.es = 1;
77210284Sjmallett    pcie_addr.io.port = pcie_port;
78210284Sjmallett    return pcie_addr.u64;
79210284Sjmallett}
80210284Sjmallett
81210284Sjmallett
82210284Sjmallett/**
83210284Sjmallett * Size of the IO address region returned at address
84210284Sjmallett * cvmx_pcie_get_io_base_address()
85210284Sjmallett *
86210284Sjmallett * @param pcie_port PCIe port the IO is for
87210284Sjmallett *
88210284Sjmallett * @return Size of the IO window
89210284Sjmallett */
90210284Sjmallettuint64_t cvmx_pcie_get_io_size(int pcie_port)
91210284Sjmallett{
92210284Sjmallett    return 1ull<<32;
93210284Sjmallett}
94210284Sjmallett
95210284Sjmallett
96210284Sjmallett/**
97210284Sjmallett * Return the Core virtual base address for PCIe MEM access. Memory is
98210284Sjmallett * read/written as an offset from this address.
99210284Sjmallett *
100210284Sjmallett * @param pcie_port PCIe port the IO is for
101210284Sjmallett *
102210284Sjmallett * @return 64bit Octeon IO base address for read/write
103210284Sjmallett */
104210284Sjmallettuint64_t cvmx_pcie_get_mem_base_address(int pcie_port)
105210284Sjmallett{
106210284Sjmallett    cvmx_pcie_address_t pcie_addr;
107210284Sjmallett    pcie_addr.u64 = 0;
108210284Sjmallett    pcie_addr.mem.upper = 0;
109210284Sjmallett    pcie_addr.mem.io = 1;
110210284Sjmallett    pcie_addr.mem.did = 3;
111210284Sjmallett    pcie_addr.mem.subdid = 3 + pcie_port;
112210284Sjmallett    return pcie_addr.u64;
113210284Sjmallett}
114210284Sjmallett
115210284Sjmallett
116210284Sjmallett/**
117210284Sjmallett * Size of the Mem address region returned at address
118210284Sjmallett * cvmx_pcie_get_mem_base_address()
119210284Sjmallett *
120210284Sjmallett * @param pcie_port PCIe port the IO is for
121210284Sjmallett *
122210284Sjmallett * @return Size of the Mem window
123210284Sjmallett */
124210284Sjmallettuint64_t cvmx_pcie_get_mem_size(int pcie_port)
125210284Sjmallett{
126210284Sjmallett    return 1ull<<36;
127210284Sjmallett}
128210284Sjmallett
129210284Sjmallett
130210284Sjmallett/**
131210284Sjmallett * @INTERNAL
132210284Sjmallett * Initialize the RC config space CSRs
133210284Sjmallett *
134210284Sjmallett * @param pcie_port PCIe port to initialize
135210284Sjmallett */
136210284Sjmallettstatic void __cvmx_pcie_rc_initialize_config_space(int pcie_port)
137210284Sjmallett{
138210284Sjmallett    /* Max Payload Size (PCIE*_CFG030[MPS]) */
139210284Sjmallett    /* Max Read Request Size (PCIE*_CFG030[MRRS]) */
140210284Sjmallett    /* Relaxed-order, no-snoop enables (PCIE*_CFG030[RO_EN,NS_EN] */
141210284Sjmallett    /* Error Message Enables (PCIE*_CFG030[CE_EN,NFE_EN,FE_EN,UR_EN]) */
142210284Sjmallett    {
143210284Sjmallett        cvmx_pciercx_cfg030_t pciercx_cfg030;
144210284Sjmallett        pciercx_cfg030.u32 = cvmx_pcie_cfgx_read(pcie_port, CVMX_PCIERCX_CFG030(pcie_port));
145210284Sjmallett        pciercx_cfg030.s.mps = 0; /* Max payload size = 128 bytes for best Octeon DMA performance */
146210284Sjmallett        pciercx_cfg030.s.mrrs = 0; /* Max read request size = 128 bytes for best Octeon DMA performance */
147210284Sjmallett        pciercx_cfg030.s.ro_en = 1; /* Enable relaxed order processing. This will allow devices to affect read response ordering */
148210284Sjmallett        pciercx_cfg030.s.ns_en = 1; /* Enable no snoop processing. Not used by Octeon */
149210284Sjmallett        pciercx_cfg030.s.ce_en = 1; /* Correctable error reporting enable. */
150210284Sjmallett        pciercx_cfg030.s.nfe_en = 1; /* Non-fatal error reporting enable. */
151210284Sjmallett        pciercx_cfg030.s.fe_en = 1; /* Fatal error reporting enable. */
152210284Sjmallett        pciercx_cfg030.s.ur_en = 1; /* Unsupported request reporting enable. */
153210284Sjmallett        cvmx_pcie_cfgx_write(pcie_port, CVMX_PCIERCX_CFG030(pcie_port), pciercx_cfg030.u32);
154210284Sjmallett    }
155210284Sjmallett
156210284Sjmallett    /* Max Payload Size (NPEI_CTL_STATUS2[MPS]) must match PCIE*_CFG030[MPS] */
157210284Sjmallett    /* Max Read Request Size (NPEI_CTL_STATUS2[MRRS]) must not exceed PCIE*_CFG030[MRRS] */
158210284Sjmallett    {
159210284Sjmallett        cvmx_npei_ctl_status2_t npei_ctl_status2;
160210284Sjmallett        npei_ctl_status2.u64 = cvmx_read_csr(CVMX_PEXP_NPEI_CTL_STATUS2);
161210284Sjmallett        npei_ctl_status2.s.mps = 0; /* Max payload size = 128 bytes for best Octeon DMA performance */
162210284Sjmallett        npei_ctl_status2.s.mrrs = 0; /* Max read request size = 128 bytes for best Octeon DMA performance */
163210284Sjmallett        cvmx_write_csr(CVMX_PEXP_NPEI_CTL_STATUS2, npei_ctl_status2.u64);
164210284Sjmallett    }
165210284Sjmallett
166210284Sjmallett    /* ECRC Generation (PCIE*_CFG070[GE,CE]) */
167210284Sjmallett    {
168210284Sjmallett        cvmx_pciercx_cfg070_t pciercx_cfg070;
169210284Sjmallett        pciercx_cfg070.u32 = cvmx_pcie_cfgx_read(pcie_port, CVMX_PCIERCX_CFG070(pcie_port));
170210284Sjmallett        pciercx_cfg070.s.ge = 1; /* ECRC generation enable. */
171210284Sjmallett        pciercx_cfg070.s.ce = 1; /* ECRC check enable. */
172210284Sjmallett        cvmx_pcie_cfgx_write(pcie_port, CVMX_PCIERCX_CFG070(pcie_port), pciercx_cfg070.u32);
173210284Sjmallett    }
174210284Sjmallett
175210284Sjmallett    /* Access Enables (PCIE*_CFG001[MSAE,ME]) */
176210284Sjmallett        /* ME and MSAE should always be set. */
177210284Sjmallett    /* Interrupt Disable (PCIE*_CFG001[I_DIS]) */
178210284Sjmallett    /* System Error Message Enable (PCIE*_CFG001[SEE]) */
179210284Sjmallett    {
180210284Sjmallett        cvmx_pciercx_cfg001_t pciercx_cfg001;
181210284Sjmallett        pciercx_cfg001.u32 = cvmx_pcie_cfgx_read(pcie_port, CVMX_PCIERCX_CFG001(pcie_port));
182210284Sjmallett        pciercx_cfg001.s.msae = 1; /* Memory space enable. */
183210284Sjmallett        pciercx_cfg001.s.me = 1; /* Bus master enable. */
184210284Sjmallett        pciercx_cfg001.s.i_dis = 1; /* INTx assertion disable. */
185210284Sjmallett        pciercx_cfg001.s.see = 1; /* SERR# enable */
186210284Sjmallett        cvmx_pcie_cfgx_write(pcie_port, CVMX_PCIERCX_CFG001(pcie_port), pciercx_cfg001.u32);
187210284Sjmallett    }
188210284Sjmallett
189210284Sjmallett
190210284Sjmallett    /* Advanced Error Recovery Message Enables */
191210284Sjmallett    /* (PCIE*_CFG066,PCIE*_CFG067,PCIE*_CFG069) */
192210284Sjmallett    cvmx_pcie_cfgx_write(pcie_port, CVMX_PCIERCX_CFG066(pcie_port), 0);
193210284Sjmallett    /* Use CVMX_PCIERCX_CFG067 hardware default */
194210284Sjmallett    cvmx_pcie_cfgx_write(pcie_port, CVMX_PCIERCX_CFG069(pcie_port), 0);
195210284Sjmallett
196210284Sjmallett
197210284Sjmallett    /* Active State Power Management (PCIE*_CFG032[ASLPC]) */
198210284Sjmallett    {
199210284Sjmallett        cvmx_pciercx_cfg032_t pciercx_cfg032;
200210284Sjmallett        pciercx_cfg032.u32 = cvmx_pcie_cfgx_read(pcie_port, CVMX_PCIERCX_CFG032(pcie_port));
201210284Sjmallett        pciercx_cfg032.s.aslpc = 0; /* Active state Link PM control. */
202210284Sjmallett        cvmx_pcie_cfgx_write(pcie_port, CVMX_PCIERCX_CFG032(pcie_port), pciercx_cfg032.u32);
203210284Sjmallett    }
204210284Sjmallett
205210284Sjmallett    /* Entrance Latencies (PCIE*_CFG451[L0EL,L1EL]) */
206210284Sjmallett    // FIXME: Anything needed here?
207210284Sjmallett
208210284Sjmallett    /* Link Width Mode (PCIERCn_CFG452[LME]) - Set during cvmx_pcie_rc_initialize_link() */
209210284Sjmallett    /* Primary Bus Number (PCIERCn_CFG006[PBNUM]) */
210210284Sjmallett    {
211210284Sjmallett        /* We set the primary bus number to 1 so IDT bridges are happy. They don't like zero */
212210284Sjmallett        cvmx_pciercx_cfg006_t pciercx_cfg006;
213210284Sjmallett        pciercx_cfg006.u32 = 0;
214210284Sjmallett        pciercx_cfg006.s.pbnum = 1;
215210284Sjmallett        pciercx_cfg006.s.sbnum = 1;
216210284Sjmallett        pciercx_cfg006.s.subbnum = 1;
217210284Sjmallett        cvmx_pcie_cfgx_write(pcie_port, CVMX_PCIERCX_CFG006(pcie_port), pciercx_cfg006.u32);
218210284Sjmallett    }
219210284Sjmallett
220210284Sjmallett    /* Memory-mapped I/O BAR (PCIERCn_CFG008) */
221210284Sjmallett    /* Most applications should disable the memory-mapped I/O BAR by */
222210284Sjmallett    /* setting PCIERCn_CFG008[ML_ADDR] < PCIERCn_CFG008[MB_ADDR] */
223210284Sjmallett    {
224210284Sjmallett        cvmx_pciercx_cfg008_t pciercx_cfg008;
225210284Sjmallett        pciercx_cfg008.u32 = 0;
226210284Sjmallett        pciercx_cfg008.s.mb_addr = 0x100;
227210284Sjmallett        pciercx_cfg008.s.ml_addr = 0;
228210284Sjmallett        cvmx_pcie_cfgx_write(pcie_port, CVMX_PCIERCX_CFG008(pcie_port), pciercx_cfg008.u32);
229210284Sjmallett    }
230210284Sjmallett
231210284Sjmallett    /* Prefetchable BAR (PCIERCn_CFG009,PCIERCn_CFG010,PCIERCn_CFG011) */
232210284Sjmallett    /* Most applications should disable the prefetchable BAR by setting */
233210284Sjmallett    /* PCIERCn_CFG011[UMEM_LIMIT],PCIERCn_CFG009[LMEM_LIMIT] < */
234210284Sjmallett    /* PCIERCn_CFG010[UMEM_BASE],PCIERCn_CFG009[LMEM_BASE] */
235210284Sjmallett    {
236210284Sjmallett        cvmx_pciercx_cfg009_t pciercx_cfg009;
237210284Sjmallett        cvmx_pciercx_cfg010_t pciercx_cfg010;
238210284Sjmallett        cvmx_pciercx_cfg011_t pciercx_cfg011;
239210284Sjmallett        pciercx_cfg009.u32 = cvmx_pcie_cfgx_read(pcie_port, CVMX_PCIERCX_CFG009(pcie_port));
240210284Sjmallett        pciercx_cfg010.u32 = cvmx_pcie_cfgx_read(pcie_port, CVMX_PCIERCX_CFG010(pcie_port));
241210284Sjmallett        pciercx_cfg011.u32 = cvmx_pcie_cfgx_read(pcie_port, CVMX_PCIERCX_CFG011(pcie_port));
242210284Sjmallett        pciercx_cfg009.s.lmem_base = 0x100;
243210284Sjmallett        pciercx_cfg009.s.lmem_limit = 0;
244210284Sjmallett        pciercx_cfg010.s.umem_base = 0x100;
245210284Sjmallett        pciercx_cfg011.s.umem_limit = 0;
246210284Sjmallett        cvmx_pcie_cfgx_write(pcie_port, CVMX_PCIERCX_CFG009(pcie_port), pciercx_cfg009.u32);
247210284Sjmallett        cvmx_pcie_cfgx_write(pcie_port, CVMX_PCIERCX_CFG010(pcie_port), pciercx_cfg010.u32);
248210284Sjmallett        cvmx_pcie_cfgx_write(pcie_port, CVMX_PCIERCX_CFG011(pcie_port), pciercx_cfg011.u32);
249210284Sjmallett    }
250210284Sjmallett
251210284Sjmallett    /* System Error Interrupt Enables (PCIERCn_CFG035[SECEE,SEFEE,SENFEE]) */
252210284Sjmallett    /* PME Interrupt Enables (PCIERCn_CFG035[PMEIE]) */
253210284Sjmallett    {
254210284Sjmallett        cvmx_pciercx_cfg035_t pciercx_cfg035;
255210284Sjmallett        pciercx_cfg035.u32 = cvmx_pcie_cfgx_read(pcie_port, CVMX_PCIERCX_CFG035(pcie_port));
256210284Sjmallett        pciercx_cfg035.s.secee = 1; /* System error on correctable error enable. */
257210284Sjmallett        pciercx_cfg035.s.sefee = 1; /* System error on fatal error enable. */
258210284Sjmallett        pciercx_cfg035.s.senfee = 1; /* System error on non-fatal error enable. */
259210284Sjmallett        pciercx_cfg035.s.pmeie = 1; /* PME interrupt enable. */
260210284Sjmallett        cvmx_pcie_cfgx_write(pcie_port, CVMX_PCIERCX_CFG035(pcie_port), pciercx_cfg035.u32);
261210284Sjmallett    }
262210284Sjmallett
263210284Sjmallett    /* Advanced Error Recovery Interrupt Enables */
264210284Sjmallett    /* (PCIERCn_CFG075[CERE,NFERE,FERE]) */
265210284Sjmallett    {
266210284Sjmallett        cvmx_pciercx_cfg075_t pciercx_cfg075;
267210284Sjmallett        pciercx_cfg075.u32 = cvmx_pcie_cfgx_read(pcie_port, CVMX_PCIERCX_CFG075(pcie_port));
268210284Sjmallett        pciercx_cfg075.s.cere = 1; /* Correctable error reporting enable. */
269210284Sjmallett        pciercx_cfg075.s.nfere = 1; /* Non-fatal error reporting enable. */
270210284Sjmallett        pciercx_cfg075.s.fere = 1; /* Fatal error reporting enable. */
271210284Sjmallett        cvmx_pcie_cfgx_write(pcie_port, CVMX_PCIERCX_CFG075(pcie_port), pciercx_cfg075.u32);
272210284Sjmallett    }
273210284Sjmallett
274210284Sjmallett    /* HP Interrupt Enables (PCIERCn_CFG034[HPINT_EN], */
275210284Sjmallett    /* PCIERCn_CFG034[DLLS_EN,CCINT_EN]) */
276210284Sjmallett    {
277210284Sjmallett        cvmx_pciercx_cfg034_t pciercx_cfg034;
278210284Sjmallett        pciercx_cfg034.u32 = cvmx_pcie_cfgx_read(pcie_port, CVMX_PCIERCX_CFG034(pcie_port));
279210284Sjmallett        pciercx_cfg034.s.hpint_en = 1; /* Hot-plug interrupt enable. */
280210284Sjmallett        pciercx_cfg034.s.dlls_en = 1; /* Data Link Layer state changed enable */
281210284Sjmallett        pciercx_cfg034.s.ccint_en = 1; /* Command completed interrupt enable. */
282210284Sjmallett        cvmx_pcie_cfgx_write(pcie_port, CVMX_PCIERCX_CFG034(pcie_port), pciercx_cfg034.u32);
283210284Sjmallett    }
284210284Sjmallett}
285210284Sjmallett
286210284Sjmallett
287210284Sjmallett/**
288210284Sjmallett * @INTERNAL
289210284Sjmallett * Initialize a host mode PCIe link. This function takes a PCIe
290210284Sjmallett * port from reset to a link up state. Software can then begin
291210284Sjmallett * configuring the rest of the link.
292210284Sjmallett *
293210284Sjmallett * @param pcie_port PCIe port to initialize
294210284Sjmallett *
295210284Sjmallett * @return Zero on success
296210284Sjmallett */
297210284Sjmallettstatic int __cvmx_pcie_rc_initialize_link(int pcie_port)
298210284Sjmallett{
299210284Sjmallett    uint64_t start_cycle;
300210284Sjmallett    cvmx_pescx_ctl_status_t pescx_ctl_status;
301210284Sjmallett    cvmx_pciercx_cfg452_t pciercx_cfg452;
302210284Sjmallett    cvmx_pciercx_cfg032_t pciercx_cfg032;
303210284Sjmallett    cvmx_pciercx_cfg448_t pciercx_cfg448;
304210284Sjmallett
305210284Sjmallett    /* Set the lane width */
306210284Sjmallett    pciercx_cfg452.u32 = cvmx_pcie_cfgx_read(pcie_port, CVMX_PCIERCX_CFG452(pcie_port));
307210284Sjmallett    pescx_ctl_status.u64 = cvmx_read_csr(CVMX_PESCX_CTL_STATUS(pcie_port));
308210284Sjmallett    if (pescx_ctl_status.s.qlm_cfg == 0)
309210284Sjmallett    {
310210284Sjmallett        /* We're in 8 lane (56XX) or 4 lane (54XX) mode */
311210284Sjmallett        pciercx_cfg452.s.lme = 0xf;
312210284Sjmallett    }
313210284Sjmallett    else
314210284Sjmallett    {
315210284Sjmallett        /* We're in 4 lane (56XX) or 2 lane (52XX) mode */
316210284Sjmallett        pciercx_cfg452.s.lme = 0x7;
317210284Sjmallett    }
318210284Sjmallett    cvmx_pcie_cfgx_write(pcie_port, CVMX_PCIERCX_CFG452(pcie_port), pciercx_cfg452.u32);
319210284Sjmallett
320210284Sjmallett    /* CN52XX pass 1.x has an errata where length mismatches on UR responses can
321210284Sjmallett        cause bus errors on 64bit memory reads. Turning off length error
322210284Sjmallett        checking fixes this */
323210284Sjmallett    if (OCTEON_IS_MODEL(OCTEON_CN52XX_PASS1_X))
324210284Sjmallett    {
325210284Sjmallett        cvmx_pciercx_cfg455_t pciercx_cfg455;
326210284Sjmallett        pciercx_cfg455.u32 = cvmx_pcie_cfgx_read(pcie_port, CVMX_PCIERCX_CFG455(pcie_port));
327210284Sjmallett        pciercx_cfg455.s.m_cpl_len_err = 1;
328210284Sjmallett        cvmx_pcie_cfgx_write(pcie_port, CVMX_PCIERCX_CFG455(pcie_port), pciercx_cfg455.u32);
329210284Sjmallett    }
330210284Sjmallett
331210284Sjmallett    /* Lane swap needs to be manually enabled for CN52XX */
332210284Sjmallett    if (OCTEON_IS_MODEL(OCTEON_CN52XX) && (pcie_port == 1))
333210284Sjmallett    {
334210284Sjmallett      pescx_ctl_status.s.lane_swp = 1;
335210284Sjmallett      cvmx_write_csr(CVMX_PESCX_CTL_STATUS(pcie_port),pescx_ctl_status.u64);
336210284Sjmallett    }
337210284Sjmallett
338210284Sjmallett    /* Bring up the link */
339210284Sjmallett    pescx_ctl_status.u64 = cvmx_read_csr(CVMX_PESCX_CTL_STATUS(pcie_port));
340210284Sjmallett    pescx_ctl_status.s.lnk_enb = 1;
341210284Sjmallett    cvmx_write_csr(CVMX_PESCX_CTL_STATUS(pcie_port), pescx_ctl_status.u64);
342210284Sjmallett
343210284Sjmallett    /* CN52XX pass 1.0: Due to a bug in 2nd order CDR, it needs to be disabled */
344210284Sjmallett    if (OCTEON_IS_MODEL(OCTEON_CN52XX_PASS1_0))
345210284Sjmallett        __cvmx_helper_errata_qlm_disable_2nd_order_cdr(0);
346210284Sjmallett
347210284Sjmallett    /* Wait for the link to come up */
348210284Sjmallett    start_cycle = cvmx_get_cycle();
349210284Sjmallett    do
350210284Sjmallett    {
351210284Sjmallett        if (cvmx_get_cycle() - start_cycle > 2*cvmx_sysinfo_get()->cpu_clock_hz)
352210284Sjmallett        {
353210284Sjmallett            cvmx_dprintf("PCIe: Port %d link timeout\n", pcie_port);
354210284Sjmallett            return -1;
355210284Sjmallett        }
356210284Sjmallett        cvmx_wait(10000);
357210284Sjmallett        pciercx_cfg032.u32 = cvmx_pcie_cfgx_read(pcie_port, CVMX_PCIERCX_CFG032(pcie_port));
358210284Sjmallett    } while (pciercx_cfg032.s.dlla == 0);
359210284Sjmallett
360210284Sjmallett    /* Update the Replay Time Limit. Empirically, some PCIe devices take a
361210284Sjmallett        little longer to respond than expected under load. As a workaround for
362210284Sjmallett        this we configure the Replay Time Limit to the value expected for a 512
363210284Sjmallett        byte MPS instead of our actual 256 byte MPS. The numbers below are
364210284Sjmallett        directly from the PCIe spec table 3-4 */
365210284Sjmallett    pciercx_cfg448.u32 = cvmx_pcie_cfgx_read(pcie_port, CVMX_PCIERCX_CFG448(pcie_port));
366210284Sjmallett    switch (pciercx_cfg032.s.nlw)
367210284Sjmallett    {
368210284Sjmallett        case 1: /* 1 lane */
369210284Sjmallett            pciercx_cfg448.s.rtl = 1677;
370210284Sjmallett            break;
371210284Sjmallett        case 2: /* 2 lanes */
372210284Sjmallett            pciercx_cfg448.s.rtl = 867;
373210284Sjmallett            break;
374210284Sjmallett        case 4: /* 4 lanes */
375210284Sjmallett            pciercx_cfg448.s.rtl = 462;
376210284Sjmallett            break;
377210284Sjmallett        case 8: /* 8 lanes */
378210284Sjmallett            pciercx_cfg448.s.rtl = 258;
379210284Sjmallett            break;
380210284Sjmallett    }
381210284Sjmallett    cvmx_pcie_cfgx_write(pcie_port, CVMX_PCIERCX_CFG448(pcie_port), pciercx_cfg448.u32);
382210284Sjmallett
383210284Sjmallett    return 0;
384210284Sjmallett}
385210284Sjmallett
386210284Sjmallett
387210284Sjmallett/**
388210284Sjmallett * Initialize a PCIe port for use in host(RC) mode. It doesn't enumerate the bus.
389210284Sjmallett *
390210284Sjmallett * @param pcie_port PCIe port to initialize
391210284Sjmallett *
392210284Sjmallett * @return Zero on success
393210284Sjmallett */
394210284Sjmallettint cvmx_pcie_rc_initialize(int pcie_port)
395210284Sjmallett{
396210284Sjmallett    int i;
397210284Sjmallett    cvmx_ciu_soft_prst_t ciu_soft_prst;
398210284Sjmallett    cvmx_pescx_bist_status_t pescx_bist_status;
399210284Sjmallett    cvmx_pescx_bist_status2_t pescx_bist_status2;
400210284Sjmallett    cvmx_npei_ctl_status_t npei_ctl_status;
401210284Sjmallett    cvmx_npei_mem_access_ctl_t npei_mem_access_ctl;
402210284Sjmallett    cvmx_npei_mem_access_subidx_t mem_access_subid;
403210284Sjmallett    cvmx_npei_dbg_data_t npei_dbg_data;
404210284Sjmallett    cvmx_pescx_ctl_status2_t pescx_ctl_status2;
405210284Sjmallett    cvmx_pciercx_cfg032_t pciercx_cfg032;
406210284Sjmallett
407210284Sjmallettretry:
408210284Sjmallett    /* Make sure we aren't trying to setup a target mode interface in host mode */
409210284Sjmallett    npei_ctl_status.u64 = cvmx_read_csr(CVMX_PEXP_NPEI_CTL_STATUS);
410210284Sjmallett    if ((pcie_port==0) && !npei_ctl_status.s.host_mode)
411210284Sjmallett    {
412210284Sjmallett        cvmx_dprintf("PCIe: ERROR: cvmx_pcie_rc_initialize() called on port0, but port0 is not in host mode\n");
413210284Sjmallett        return -1;
414210284Sjmallett    }
415210284Sjmallett
416210284Sjmallett    /* Make sure a CN52XX isn't trying to bring up port 1 when it is disabled */
417210284Sjmallett    if (OCTEON_IS_MODEL(OCTEON_CN52XX))
418210284Sjmallett    {
419210284Sjmallett        npei_dbg_data.u64 = cvmx_read_csr(CVMX_PEXP_NPEI_DBG_DATA);
420210284Sjmallett        if ((pcie_port==1) && npei_dbg_data.cn52xx.qlm0_link_width)
421210284Sjmallett        {
422210284Sjmallett            cvmx_dprintf("PCIe: ERROR: cvmx_pcie_rc_initialize() called on port1, but port1 is disabled\n");
423210284Sjmallett            return -1;
424210284Sjmallett        }
425210284Sjmallett    }
426210284Sjmallett
427210284Sjmallett    /* PCIe switch arbitration mode. '0' == fixed priority NPEI, PCIe0, then PCIe1. '1' == round robin. */
428210284Sjmallett    npei_ctl_status.s.arb = 1;
429210284Sjmallett    /* Allow up to 0x20 config retries */
430210284Sjmallett    npei_ctl_status.s.cfg_rtry = 0x20;
431210284Sjmallett    /* CN52XX pass1.x has an errata where P0_NTAGS and P1_NTAGS don't reset */
432210284Sjmallett    if (OCTEON_IS_MODEL(OCTEON_CN52XX_PASS1_X))
433210284Sjmallett    {
434210284Sjmallett        npei_ctl_status.s.p0_ntags = 0x20;
435210284Sjmallett        npei_ctl_status.s.p1_ntags = 0x20;
436210284Sjmallett    }
437210284Sjmallett    cvmx_write_csr(CVMX_PEXP_NPEI_CTL_STATUS, npei_ctl_status.u64);
438210284Sjmallett
439210284Sjmallett    /* Bring the PCIe out of reset */
440210284Sjmallett    if (cvmx_sysinfo_get()->board_type == CVMX_BOARD_TYPE_EBH5200)
441210284Sjmallett    {
442210284Sjmallett        /* The EBH5200 board swapped the PCIe reset lines on the board. As a
443210284Sjmallett            workaround for this bug, we bring both PCIe ports out of reset at
444210284Sjmallett            the same time instead of on separate calls. So for port 0, we bring
445210284Sjmallett            both out of reset and do nothing on port 1 */
446210284Sjmallett        if (pcie_port == 0)
447210284Sjmallett        {
448210284Sjmallett            ciu_soft_prst.u64 = cvmx_read_csr(CVMX_CIU_SOFT_PRST);
449210284Sjmallett            /* After a chip reset the PCIe will also be in reset. If it isn't,
450210284Sjmallett                most likely someone is trying to init it again without a proper
451210284Sjmallett                PCIe reset */
452210284Sjmallett            if (ciu_soft_prst.s.soft_prst == 0)
453210284Sjmallett            {
454210284Sjmallett		/* Reset the ports */
455210284Sjmallett		ciu_soft_prst.s.soft_prst = 1;
456210284Sjmallett		cvmx_write_csr(CVMX_CIU_SOFT_PRST, ciu_soft_prst.u64);
457210284Sjmallett		ciu_soft_prst.u64 = cvmx_read_csr(CVMX_CIU_SOFT_PRST1);
458210284Sjmallett		ciu_soft_prst.s.soft_prst = 1;
459210284Sjmallett		cvmx_write_csr(CVMX_CIU_SOFT_PRST1, ciu_soft_prst.u64);
460210284Sjmallett		/* Wait until pcie resets the ports. */
461210284Sjmallett		cvmx_wait_usec(2000);
462210284Sjmallett            }
463210284Sjmallett            ciu_soft_prst.u64 = cvmx_read_csr(CVMX_CIU_SOFT_PRST1);
464210284Sjmallett            ciu_soft_prst.s.soft_prst = 0;
465210284Sjmallett            cvmx_write_csr(CVMX_CIU_SOFT_PRST1, ciu_soft_prst.u64);
466210284Sjmallett            ciu_soft_prst.u64 = cvmx_read_csr(CVMX_CIU_SOFT_PRST);
467210284Sjmallett            ciu_soft_prst.s.soft_prst = 0;
468210284Sjmallett            cvmx_write_csr(CVMX_CIU_SOFT_PRST, ciu_soft_prst.u64);
469210284Sjmallett        }
470210284Sjmallett    }
471210284Sjmallett    else
472210284Sjmallett    {
473210284Sjmallett        /* The normal case: The PCIe ports are completely separate and can be
474210284Sjmallett            brought out of reset independently */
475210284Sjmallett        if (pcie_port)
476210284Sjmallett            ciu_soft_prst.u64 = cvmx_read_csr(CVMX_CIU_SOFT_PRST1);
477210284Sjmallett        else
478210284Sjmallett            ciu_soft_prst.u64 = cvmx_read_csr(CVMX_CIU_SOFT_PRST);
479210284Sjmallett        /* After a chip reset the PCIe will also be in reset. If it isn't,
480210284Sjmallett            most likely someone is trying to init it again without a proper
481210284Sjmallett            PCIe reset */
482210284Sjmallett        if (ciu_soft_prst.s.soft_prst == 0)
483210284Sjmallett        {
484210284Sjmallett	    /* Reset the port */
485210284Sjmallett	    ciu_soft_prst.s.soft_prst = 1;
486210284Sjmallett	    if (pcie_port)
487210284Sjmallett		cvmx_write_csr(CVMX_CIU_SOFT_PRST1, ciu_soft_prst.u64);
488210284Sjmallett 	    else
489210284Sjmallett		cvmx_write_csr(CVMX_CIU_SOFT_PRST, ciu_soft_prst.u64);
490210284Sjmallett	    /* Wait until pcie resets the ports. */
491210284Sjmallett	    cvmx_wait_usec(2000);
492210284Sjmallett        }
493210284Sjmallett        if (pcie_port)
494210284Sjmallett        {
495210284Sjmallett            ciu_soft_prst.u64 = cvmx_read_csr(CVMX_CIU_SOFT_PRST1);
496210284Sjmallett            ciu_soft_prst.s.soft_prst = 0;
497210284Sjmallett            cvmx_write_csr(CVMX_CIU_SOFT_PRST1, ciu_soft_prst.u64);
498210284Sjmallett        }
499210284Sjmallett        else
500210284Sjmallett        {
501210284Sjmallett            ciu_soft_prst.u64 = cvmx_read_csr(CVMX_CIU_SOFT_PRST);
502210284Sjmallett            ciu_soft_prst.s.soft_prst = 0;
503210284Sjmallett            cvmx_write_csr(CVMX_CIU_SOFT_PRST, ciu_soft_prst.u64);
504210284Sjmallett        }
505210284Sjmallett    }
506210284Sjmallett
507210284Sjmallett    /* Wait for PCIe reset to complete. Due to errata PCIE-700, we don't poll
508210284Sjmallett       PESCX_CTL_STATUS2[PCIERST], but simply wait a fixed number of cycles */
509210284Sjmallett    cvmx_wait(400000);
510210284Sjmallett
511210284Sjmallett    /* PESCX_BIST_STATUS2[PCLK_RUN] was missing on pass 1 of CN56XX and
512210284Sjmallett        CN52XX, so we only probe it on newer chips */
513210284Sjmallett    if (!OCTEON_IS_MODEL(OCTEON_CN56XX_PASS1_X) && !OCTEON_IS_MODEL(OCTEON_CN52XX_PASS1_X))
514210284Sjmallett    {
515210284Sjmallett        /* Clear PCLK_RUN so we can check if the clock is running */
516210284Sjmallett        pescx_ctl_status2.u64 = cvmx_read_csr(CVMX_PESCX_CTL_STATUS2(pcie_port));
517210284Sjmallett        pescx_ctl_status2.s.pclk_run = 1;
518210284Sjmallett        cvmx_write_csr(CVMX_PESCX_CTL_STATUS2(pcie_port), pescx_ctl_status2.u64);
519210284Sjmallett        /* Now that we cleared PCLK_RUN, wait for it to be set again telling
520210284Sjmallett            us the clock is running */
521210284Sjmallett        if (CVMX_WAIT_FOR_FIELD64(CVMX_PESCX_CTL_STATUS2(pcie_port),
522210284Sjmallett            cvmx_pescx_ctl_status2_t, pclk_run, ==, 1, 10000))
523210284Sjmallett        {
524210284Sjmallett            cvmx_dprintf("PCIe: Port %d isn't clocked, skipping.\n", pcie_port);
525210284Sjmallett            return -1;
526210284Sjmallett        }
527210284Sjmallett    }
528210284Sjmallett
529210284Sjmallett    /* Check and make sure PCIe came out of reset. If it doesn't the board
530210284Sjmallett        probably hasn't wired the clocks up and the interface should be
531210284Sjmallett        skipped */
532210284Sjmallett    pescx_ctl_status2.u64 = cvmx_read_csr(CVMX_PESCX_CTL_STATUS2(pcie_port));
533210284Sjmallett    if (pescx_ctl_status2.s.pcierst)
534210284Sjmallett    {
535210284Sjmallett        cvmx_dprintf("PCIe: Port %d stuck in reset, skipping.\n", pcie_port);
536210284Sjmallett        return -1;
537210284Sjmallett    }
538210284Sjmallett
539210284Sjmallett    /* Check BIST2 status. If any bits are set skip this interface. This
540210284Sjmallett        is an attempt to catch PCIE-813 on pass 1 parts */
541210284Sjmallett    pescx_bist_status2.u64 = cvmx_read_csr(CVMX_PESCX_BIST_STATUS2(pcie_port));
542210284Sjmallett    if (pescx_bist_status2.u64)
543210284Sjmallett    {
544210284Sjmallett        cvmx_dprintf("PCIe: Port %d BIST2 failed. Most likely this port isn't hooked up, skipping.\n", pcie_port);
545210284Sjmallett        return -1;
546210284Sjmallett    }
547210284Sjmallett
548210284Sjmallett    /* Check BIST status */
549210284Sjmallett    pescx_bist_status.u64 = cvmx_read_csr(CVMX_PESCX_BIST_STATUS(pcie_port));
550210284Sjmallett    if (pescx_bist_status.u64)
551210284Sjmallett        cvmx_dprintf("PCIe: BIST FAILED for port %d (0x%016llx)\n", pcie_port, CAST64(pescx_bist_status.u64));
552210284Sjmallett
553210284Sjmallett    /* Initialize the config space CSRs */
554210284Sjmallett    __cvmx_pcie_rc_initialize_config_space(pcie_port);
555210284Sjmallett
556210284Sjmallett    /* Bring the link up */
557210284Sjmallett    if (__cvmx_pcie_rc_initialize_link(pcie_port))
558210284Sjmallett    {
559210284Sjmallett        cvmx_dprintf("PCIe: ERROR: cvmx_pcie_rc_initialize_link() failed\n");
560210284Sjmallett        return -1;
561210284Sjmallett    }
562210284Sjmallett
563210284Sjmallett    /* Store merge control (NPEI_MEM_ACCESS_CTL[TIMER,MAX_WORD]) */
564210284Sjmallett    npei_mem_access_ctl.u64 = cvmx_read_csr(CVMX_PEXP_NPEI_MEM_ACCESS_CTL);
565210284Sjmallett    npei_mem_access_ctl.s.max_word = 0;     /* Allow 16 words to combine */
566210284Sjmallett    npei_mem_access_ctl.s.timer = 127;      /* Wait up to 127 cycles for more data */
567210284Sjmallett    cvmx_write_csr(CVMX_PEXP_NPEI_MEM_ACCESS_CTL, npei_mem_access_ctl.u64);
568210284Sjmallett
569210284Sjmallett    /* Setup Mem access SubDIDs */
570210284Sjmallett    mem_access_subid.u64 = 0;
571210284Sjmallett    mem_access_subid.s.port = pcie_port; /* Port the request is sent to. */
572210284Sjmallett    mem_access_subid.s.nmerge = 1;  /* Due to an errata on pass 1 chips, no merging is allowed. */
573210284Sjmallett    mem_access_subid.s.esr = 1;     /* Endian-swap for Reads. */
574210284Sjmallett    mem_access_subid.s.esw = 1;     /* Endian-swap for Writes. */
575210284Sjmallett    mem_access_subid.s.nsr = 0;     /* Enable Snooping for Reads. Octeon doesn't care, but devices might want this more conservative setting */
576210284Sjmallett    mem_access_subid.s.nsw = 0;     /* Enable Snoop for Writes. */
577210284Sjmallett    mem_access_subid.s.ror = 0;     /* Disable Relaxed Ordering for Reads. */
578210284Sjmallett    mem_access_subid.s.row = 0;     /* Disable Relaxed Ordering for Writes. */
579210284Sjmallett    mem_access_subid.s.ba = 0;      /* PCIe Adddress Bits <63:34>. */
580210284Sjmallett
581210284Sjmallett    /* Setup mem access 12-15 for port 0, 16-19 for port 1, supplying 36 bits of address space */
582210284Sjmallett    for (i=12 + pcie_port*4; i<16 + pcie_port*4; i++)
583210284Sjmallett    {
584210284Sjmallett        cvmx_write_csr(CVMX_PEXP_NPEI_MEM_ACCESS_SUBIDX(i), mem_access_subid.u64);
585210284Sjmallett        mem_access_subid.s.ba += 1; /* Set each SUBID to extend the addressable range */
586210284Sjmallett    }
587210284Sjmallett
588210284Sjmallett    /* Disable the peer to peer forwarding register. This must be setup
589210284Sjmallett        by the OS after it enumerates the bus and assigns addresses to the
590210284Sjmallett        PCIe busses */
591210284Sjmallett    for (i=0; i<4; i++)
592210284Sjmallett    {
593210284Sjmallett        cvmx_write_csr(CVMX_PESCX_P2P_BARX_START(i, pcie_port), -1);
594210284Sjmallett        cvmx_write_csr(CVMX_PESCX_P2P_BARX_END(i, pcie_port), -1);
595210284Sjmallett    }
596210284Sjmallett
597210284Sjmallett    /* Set Octeon's BAR0 to decode 0-16KB. It overlaps with Bar2 */
598210284Sjmallett    cvmx_write_csr(CVMX_PESCX_P2N_BAR0_START(pcie_port), 0);
599210284Sjmallett
600210284Sjmallett    /* Disable Octeon's BAR1. It isn't needed in RC mode since BAR2
601210284Sjmallett        maps all of memory. BAR2 also maps 256MB-512MB into the 2nd
602210284Sjmallett        256MB of memory */
603210284Sjmallett    cvmx_write_csr(CVMX_PESCX_P2N_BAR1_START(pcie_port), -1);
604210284Sjmallett
605210284Sjmallett    /* Set Octeon's BAR2 to decode 0-2^39. Bar0 and Bar1 take precedence
606210284Sjmallett        where they overlap. It also overlaps with the device addresses, so
607210284Sjmallett        make sure the peer to peer forwarding is set right */
608210284Sjmallett    cvmx_write_csr(CVMX_PESCX_P2N_BAR2_START(pcie_port), 0);
609210284Sjmallett
610210284Sjmallett    /* Setup BAR2 attributes */
611210284Sjmallett    /* Relaxed Ordering (NPEI_CTL_PORTn[PTLP_RO,CTLP_RO, WAIT_COM]) */
612210284Sjmallett    /* � PTLP_RO,CTLP_RO should normally be set (except for debug). */
613210284Sjmallett    /* � WAIT_COM=0 will likely work for all applications. */
614210284Sjmallett    /* Load completion relaxed ordering (NPEI_CTL_PORTn[WAITL_COM]) */
615210284Sjmallett    if (pcie_port)
616210284Sjmallett    {
617210284Sjmallett        cvmx_npei_ctl_port1_t npei_ctl_port;
618210284Sjmallett        npei_ctl_port.u64 = cvmx_read_csr(CVMX_PEXP_NPEI_CTL_PORT1);
619210284Sjmallett        npei_ctl_port.s.bar2_enb = 1;
620210284Sjmallett        npei_ctl_port.s.bar2_esx = 1;
621210284Sjmallett        npei_ctl_port.s.bar2_cax = 0;
622210284Sjmallett        npei_ctl_port.s.ptlp_ro = 1;
623210284Sjmallett        npei_ctl_port.s.ctlp_ro = 1;
624210284Sjmallett        npei_ctl_port.s.wait_com = 0;
625210284Sjmallett        npei_ctl_port.s.waitl_com = 0;
626210284Sjmallett        cvmx_write_csr(CVMX_PEXP_NPEI_CTL_PORT1, npei_ctl_port.u64);
627210284Sjmallett    }
628210284Sjmallett    else
629210284Sjmallett    {
630210284Sjmallett        cvmx_npei_ctl_port0_t npei_ctl_port;
631210284Sjmallett        npei_ctl_port.u64 = cvmx_read_csr(CVMX_PEXP_NPEI_CTL_PORT0);
632210284Sjmallett        npei_ctl_port.s.bar2_enb = 1;
633210284Sjmallett        npei_ctl_port.s.bar2_esx = 1;
634210284Sjmallett        npei_ctl_port.s.bar2_cax = 0;
635210284Sjmallett        npei_ctl_port.s.ptlp_ro = 1;
636210284Sjmallett        npei_ctl_port.s.ctlp_ro = 1;
637210284Sjmallett        npei_ctl_port.s.wait_com = 0;
638210284Sjmallett        npei_ctl_port.s.waitl_com = 0;
639210284Sjmallett        cvmx_write_csr(CVMX_PEXP_NPEI_CTL_PORT0, npei_ctl_port.u64);
640210284Sjmallett    }
641210284Sjmallett
642210284Sjmallett    /* Both pass 1 and pass 2 of CN52XX and CN56XX have an errata that causes
643210284Sjmallett        TLP ordering to not be preserved after multiple PCIe port resets. This
644210284Sjmallett        code detects this fault and corrects it by aligning the TLP counters
645210284Sjmallett        properly. Another link reset is then performed. See PCIE-13340 */
646210284Sjmallett    if (OCTEON_IS_MODEL(OCTEON_CN56XX_PASS2_X) || OCTEON_IS_MODEL(OCTEON_CN52XX_PASS2_X) ||
647210284Sjmallett        OCTEON_IS_MODEL(OCTEON_CN56XX_PASS1_X) || OCTEON_IS_MODEL(OCTEON_CN52XX_PASS1_X))
648210284Sjmallett    {
649210284Sjmallett        cvmx_npei_dbg_data_t dbg_data;
650210284Sjmallett        int old_in_fif_p_count;
651210284Sjmallett        int in_fif_p_count;
652210284Sjmallett        int out_p_count;
653210284Sjmallett        int in_p_offset = (OCTEON_IS_MODEL(OCTEON_CN52XX_PASS1_X) || OCTEON_IS_MODEL(OCTEON_CN56XX_PASS1_X)) ? 4 : 1;
654210284Sjmallett        int i;
655210284Sjmallett
656210284Sjmallett        /* Choose a write address of 1MB. It should be harmless as all bars
657210284Sjmallett            haven't been setup */
658210284Sjmallett        uint64_t write_address = (cvmx_pcie_get_mem_base_address(pcie_port) + 0x100000) | (1ull<<63);
659210284Sjmallett
660210284Sjmallett        /* Make sure at least in_p_offset have been executed before we try and
661210284Sjmallett            read in_fif_p_count */
662210284Sjmallett        i = in_p_offset;
663210284Sjmallett        while (i--)
664210284Sjmallett        {
665210284Sjmallett            cvmx_write64_uint32(write_address, 0);
666210284Sjmallett            cvmx_wait(10000);
667210284Sjmallett        }
668210284Sjmallett
669210284Sjmallett        /* Read the IN_FIF_P_COUNT from the debug select. IN_FIF_P_COUNT can be
670210284Sjmallett            unstable sometimes so read it twice with a write between the reads.
671210284Sjmallett            This way we can tell the value is good as it will increment by one
672210284Sjmallett            due to the write */
673210284Sjmallett        cvmx_write_csr(CVMX_PEXP_NPEI_DBG_SELECT, (pcie_port) ? 0xd7fc : 0xcffc);
674210284Sjmallett        cvmx_read_csr(CVMX_PEXP_NPEI_DBG_SELECT);
675210284Sjmallett        do
676210284Sjmallett        {
677210284Sjmallett            dbg_data.u64 = cvmx_read_csr(CVMX_PEXP_NPEI_DBG_DATA);
678210284Sjmallett            old_in_fif_p_count = dbg_data.s.data & 0xff;
679210284Sjmallett            cvmx_write64_uint32(write_address, 0);
680210284Sjmallett            cvmx_wait(10000);
681210284Sjmallett            dbg_data.u64 = cvmx_read_csr(CVMX_PEXP_NPEI_DBG_DATA);
682210284Sjmallett            in_fif_p_count = dbg_data.s.data & 0xff;
683210284Sjmallett        } while (in_fif_p_count != ((old_in_fif_p_count+1) & 0xff));
684210284Sjmallett
685210284Sjmallett        /* Update in_fif_p_count for it's offset with respect to out_p_count */
686210284Sjmallett        in_fif_p_count = (in_fif_p_count + in_p_offset) & 0xff;
687210284Sjmallett
688210284Sjmallett        /* Read the OUT_P_COUNT from the debug select */
689210284Sjmallett        cvmx_write_csr(CVMX_PEXP_NPEI_DBG_SELECT, (pcie_port) ? 0xd00f : 0xc80f);
690210284Sjmallett        cvmx_read_csr(CVMX_PEXP_NPEI_DBG_SELECT);
691210284Sjmallett        dbg_data.u64 = cvmx_read_csr(CVMX_PEXP_NPEI_DBG_DATA);
692210284Sjmallett        out_p_count = (dbg_data.s.data>>1) & 0xff;
693210284Sjmallett
694210284Sjmallett        /* Check that the two counters are aligned */
695210284Sjmallett        if (out_p_count != in_fif_p_count)
696210284Sjmallett        {
697210284Sjmallett            cvmx_dprintf("PCIe: Port %d aligning TLP counters as workaround to maintain ordering\n", pcie_port);
698210284Sjmallett            while (in_fif_p_count != 0)
699210284Sjmallett            {
700210284Sjmallett                cvmx_write64_uint32(write_address, 0);
701210284Sjmallett                cvmx_wait(10000);
702210284Sjmallett                in_fif_p_count = (in_fif_p_count + 1) & 0xff;
703210284Sjmallett            }
704210284Sjmallett            /* The EBH5200 board swapped the PCIe reset lines on the board. This
705210284Sjmallett                means we must bring both links down and up, which will cause the
706210284Sjmallett                PCIe0 to need alignment again. Lots of messages will be displayed,
707210284Sjmallett                but everything should work */
708210284Sjmallett            if ((cvmx_sysinfo_get()->board_type == CVMX_BOARD_TYPE_EBH5200) &&
709210284Sjmallett                (pcie_port == 1))
710210284Sjmallett                cvmx_pcie_rc_initialize(0);
711210284Sjmallett            /* Rety bringing this port up */
712210284Sjmallett            goto retry;
713210284Sjmallett        }
714210284Sjmallett    }
715210284Sjmallett
716210284Sjmallett    /* Display the link status */
717210284Sjmallett    pciercx_cfg032.u32 = cvmx_pcie_cfgx_read(pcie_port, CVMX_PCIERCX_CFG032(pcie_port));
718210284Sjmallett    cvmx_dprintf("PCIe: Port %d link active, %d lanes\n", pcie_port, pciercx_cfg032.s.nlw);
719210284Sjmallett
720210284Sjmallett    return 0;
721210284Sjmallett}
722210284Sjmallett
723210284Sjmallett
724210284Sjmallett/**
725210284Sjmallett * Shutdown a PCIe port and put it in reset
726210284Sjmallett *
727210284Sjmallett * @param pcie_port PCIe port to shutdown
728210284Sjmallett *
729210284Sjmallett * @return Zero on success
730210284Sjmallett */
731210284Sjmallettint cvmx_pcie_rc_shutdown(int pcie_port)
732210284Sjmallett{
733210284Sjmallett    /* Wait for all pending operations to complete */
734210284Sjmallett    if (CVMX_WAIT_FOR_FIELD64(CVMX_PESCX_CPL_LUT_VALID(pcie_port), cvmx_pescx_cpl_lut_valid_t, tag, ==, 0, 2000))
735210284Sjmallett        cvmx_dprintf("PCIe: Port %d shutdown timeout\n", pcie_port);
736210284Sjmallett
737210284Sjmallett    /* Force reset */
738210284Sjmallett    if (pcie_port)
739210284Sjmallett    {
740210284Sjmallett        cvmx_ciu_soft_prst_t ciu_soft_prst;
741210284Sjmallett        ciu_soft_prst.u64 = cvmx_read_csr(CVMX_CIU_SOFT_PRST1);
742210284Sjmallett        ciu_soft_prst.s.soft_prst = 1;
743210284Sjmallett        cvmx_write_csr(CVMX_CIU_SOFT_PRST1, ciu_soft_prst.u64);
744210284Sjmallett    }
745210284Sjmallett    else
746210284Sjmallett    {
747210284Sjmallett        cvmx_ciu_soft_prst_t ciu_soft_prst;
748210284Sjmallett        ciu_soft_prst.u64 = cvmx_read_csr(CVMX_CIU_SOFT_PRST);
749210284Sjmallett        ciu_soft_prst.s.soft_prst = 1;
750210284Sjmallett        cvmx_write_csr(CVMX_CIU_SOFT_PRST, ciu_soft_prst.u64);
751210284Sjmallett    }
752210284Sjmallett    return 0;
753210284Sjmallett}
754210284Sjmallett
755210284Sjmallett
756210284Sjmallett/**
757210284Sjmallett * @INTERNAL
758210284Sjmallett * Build a PCIe config space request address for a device
759210284Sjmallett *
760210284Sjmallett * @param pcie_port PCIe port to access
761210284Sjmallett * @param bus       Sub bus
762210284Sjmallett * @param dev       Device ID
763210284Sjmallett * @param fn        Device sub function
764210284Sjmallett * @param reg       Register to access
765210284Sjmallett *
766210284Sjmallett * @return 64bit Octeon IO address
767210284Sjmallett */
768210284Sjmallettstatic inline uint64_t __cvmx_pcie_build_config_addr(int pcie_port, int bus, int dev, int fn, int reg)
769210284Sjmallett{
770210284Sjmallett    cvmx_pcie_address_t pcie_addr;
771210284Sjmallett    cvmx_pciercx_cfg006_t pciercx_cfg006;
772210284Sjmallett
773210284Sjmallett    pciercx_cfg006.u32 = cvmx_pcie_cfgx_read(pcie_port, CVMX_PCIERCX_CFG006(pcie_port));
774210284Sjmallett    if ((bus <= pciercx_cfg006.s.pbnum) && (dev != 0))
775210284Sjmallett        return 0;
776210284Sjmallett
777210284Sjmallett    pcie_addr.u64 = 0;
778210284Sjmallett    pcie_addr.config.upper = 2;
779210284Sjmallett    pcie_addr.config.io = 1;
780210284Sjmallett    pcie_addr.config.did = 3;
781210284Sjmallett    pcie_addr.config.subdid = 1;
782210284Sjmallett    pcie_addr.config.es = 1;
783210284Sjmallett    pcie_addr.config.port = pcie_port;
784210284Sjmallett    pcie_addr.config.ty = (bus > pciercx_cfg006.s.pbnum);
785210284Sjmallett    pcie_addr.config.bus = bus;
786210284Sjmallett    pcie_addr.config.dev = dev;
787210284Sjmallett    pcie_addr.config.func = fn;
788210284Sjmallett    pcie_addr.config.reg = reg;
789210284Sjmallett    return pcie_addr.u64;
790210284Sjmallett}
791210284Sjmallett
792210284Sjmallett
793210284Sjmallett/**
794210284Sjmallett * Read 8bits from a Device's config space
795210284Sjmallett *
796210284Sjmallett * @param pcie_port PCIe port the device is on
797210284Sjmallett * @param bus       Sub bus
798210284Sjmallett * @param dev       Device ID
799210284Sjmallett * @param fn        Device sub function
800210284Sjmallett * @param reg       Register to access
801210284Sjmallett *
802210284Sjmallett * @return Result of the read
803210284Sjmallett */
804210284Sjmallettuint8_t cvmx_pcie_config_read8(int pcie_port, int bus, int dev, int fn, int reg)
805210284Sjmallett{
806210284Sjmallett    uint64_t address = __cvmx_pcie_build_config_addr(pcie_port, bus, dev, fn, reg);
807210284Sjmallett    if (address)
808210284Sjmallett        return cvmx_read64_uint8(address);
809210284Sjmallett    else
810210284Sjmallett        return 0xff;
811210284Sjmallett}
812210284Sjmallett
813210284Sjmallett
814210284Sjmallett/**
815210284Sjmallett * Read 16bits from a Device's config space
816210284Sjmallett *
817210284Sjmallett * @param pcie_port PCIe port the device is on
818210284Sjmallett * @param bus       Sub bus
819210284Sjmallett * @param dev       Device ID
820210284Sjmallett * @param fn        Device sub function
821210284Sjmallett * @param reg       Register to access
822210284Sjmallett *
823210284Sjmallett * @return Result of the read
824210284Sjmallett */
825210284Sjmallettuint16_t cvmx_pcie_config_read16(int pcie_port, int bus, int dev, int fn, int reg)
826210284Sjmallett{
827210284Sjmallett    uint64_t address = __cvmx_pcie_build_config_addr(pcie_port, bus, dev, fn, reg);
828210284Sjmallett    if (address)
829210284Sjmallett        return cvmx_le16_to_cpu(cvmx_read64_uint16(address));
830210284Sjmallett    else
831210284Sjmallett        return 0xffff;
832210284Sjmallett}
833210284Sjmallett
834210284Sjmallett
835210284Sjmallett/**
836210284Sjmallett * Read 32bits from a Device's config space
837210284Sjmallett *
838210284Sjmallett * @param pcie_port PCIe port the device is on
839210284Sjmallett * @param bus       Sub bus
840210284Sjmallett * @param dev       Device ID
841210284Sjmallett * @param fn        Device sub function
842210284Sjmallett * @param reg       Register to access
843210284Sjmallett *
844210284Sjmallett * @return Result of the read
845210284Sjmallett */
846210284Sjmallettuint32_t cvmx_pcie_config_read32(int pcie_port, int bus, int dev, int fn, int reg)
847210284Sjmallett{
848210284Sjmallett    uint64_t address = __cvmx_pcie_build_config_addr(pcie_port, bus, dev, fn, reg);
849210284Sjmallett    if (address)
850210284Sjmallett        return cvmx_le32_to_cpu(cvmx_read64_uint32(address));
851210284Sjmallett    else
852210284Sjmallett        return 0xffffffff;
853210284Sjmallett}
854210284Sjmallett
855210284Sjmallett
856210284Sjmallett/**
857210284Sjmallett * Write 8bits to a Device's config space
858210284Sjmallett *
859210284Sjmallett * @param pcie_port PCIe port the device is on
860210284Sjmallett * @param bus       Sub bus
861210284Sjmallett * @param dev       Device ID
862210284Sjmallett * @param fn        Device sub function
863210284Sjmallett * @param reg       Register to access
864210284Sjmallett * @param val       Value to write
865210284Sjmallett */
866210284Sjmallettvoid cvmx_pcie_config_write8(int pcie_port, int bus, int dev, int fn, int reg, uint8_t val)
867210284Sjmallett{
868210284Sjmallett    uint64_t address = __cvmx_pcie_build_config_addr(pcie_port, bus, dev, fn, reg);
869210284Sjmallett    if (address)
870210284Sjmallett        cvmx_write64_uint8(address, val);
871210284Sjmallett}
872210284Sjmallett
873210284Sjmallett
874210284Sjmallett/**
875210284Sjmallett * Write 16bits to a Device's config space
876210284Sjmallett *
877210284Sjmallett * @param pcie_port PCIe port the device is on
878210284Sjmallett * @param bus       Sub bus
879210284Sjmallett * @param dev       Device ID
880210284Sjmallett * @param fn        Device sub function
881210284Sjmallett * @param reg       Register to access
882210284Sjmallett * @param val       Value to write
883210284Sjmallett */
884210284Sjmallettvoid cvmx_pcie_config_write16(int pcie_port, int bus, int dev, int fn, int reg, uint16_t val)
885210284Sjmallett{
886210284Sjmallett    uint64_t address = __cvmx_pcie_build_config_addr(pcie_port, bus, dev, fn, reg);
887210284Sjmallett    if (address)
888210284Sjmallett        cvmx_write64_uint16(address, cvmx_cpu_to_le16(val));
889210284Sjmallett}
890210284Sjmallett
891210284Sjmallett
892210284Sjmallett/**
893210284Sjmallett * Write 32bits to a Device's config space
894210284Sjmallett *
895210284Sjmallett * @param pcie_port PCIe port the device is on
896210284Sjmallett * @param bus       Sub bus
897210284Sjmallett * @param dev       Device ID
898210284Sjmallett * @param fn        Device sub function
899210284Sjmallett * @param reg       Register to access
900210284Sjmallett * @param val       Value to write
901210284Sjmallett */
902210284Sjmallettvoid cvmx_pcie_config_write32(int pcie_port, int bus, int dev, int fn, int reg, uint32_t val)
903210284Sjmallett{
904210284Sjmallett    uint64_t address = __cvmx_pcie_build_config_addr(pcie_port, bus, dev, fn, reg);
905210284Sjmallett    if (address)
906210284Sjmallett        cvmx_write64_uint32(address, cvmx_cpu_to_le32(val));
907210284Sjmallett}
908210284Sjmallett
909210284Sjmallett
910210284Sjmallett/**
911210284Sjmallett * Read a PCIe config space register indirectly. This is used for
912210284Sjmallett * registers of the form PCIEEP_CFG??? and PCIERC?_CFG???.
913210284Sjmallett *
914210284Sjmallett * @param pcie_port  PCIe port to read from
915210284Sjmallett * @param cfg_offset Address to read
916210284Sjmallett *
917210284Sjmallett * @return Value read
918210284Sjmallett */
919210284Sjmallettuint32_t cvmx_pcie_cfgx_read(int pcie_port, uint32_t cfg_offset)
920210284Sjmallett{
921210284Sjmallett    cvmx_pescx_cfg_rd_t pescx_cfg_rd;
922210284Sjmallett    pescx_cfg_rd.u64 = 0;
923210284Sjmallett    pescx_cfg_rd.s.addr = cfg_offset;
924210284Sjmallett    cvmx_write_csr(CVMX_PESCX_CFG_RD(pcie_port), pescx_cfg_rd.u64);
925210284Sjmallett    pescx_cfg_rd.u64 = cvmx_read_csr(CVMX_PESCX_CFG_RD(pcie_port));
926210284Sjmallett    return pescx_cfg_rd.s.data;
927210284Sjmallett}
928210284Sjmallett
929210284Sjmallett
930210284Sjmallett/**
931210284Sjmallett * Write a PCIe config space register indirectly. This is used for
932210284Sjmallett * registers of the form PCIEEP_CFG??? and PCIERC?_CFG???.
933210284Sjmallett *
934210284Sjmallett * @param pcie_port  PCIe port to write to
935210284Sjmallett * @param cfg_offset Address to write
936210284Sjmallett * @param val        Value to write
937210284Sjmallett */
938210284Sjmallettvoid cvmx_pcie_cfgx_write(int pcie_port, uint32_t cfg_offset, uint32_t val)
939210284Sjmallett{
940210284Sjmallett    cvmx_pescx_cfg_wr_t pescx_cfg_wr;
941210284Sjmallett    pescx_cfg_wr.u64 = 0;
942210284Sjmallett    pescx_cfg_wr.s.addr = cfg_offset;
943210284Sjmallett    pescx_cfg_wr.s.data = val;
944210284Sjmallett    cvmx_write_csr(CVMX_PESCX_CFG_WR(pcie_port), pescx_cfg_wr.u64);
945210284Sjmallett}
946210284Sjmallett
947210284Sjmallett
948210284Sjmallett/**
949210284Sjmallett * Initialize a PCIe port for use in target(EP) mode.
950210284Sjmallett *
951210284Sjmallett * @return Zero on success
952210284Sjmallett */
953210284Sjmallettint cvmx_pcie_ep_initialize(void)
954210284Sjmallett{
955210284Sjmallett    int pcie_port = 0;
956210284Sjmallett    cvmx_npei_ctl_status_t npei_ctl_status;
957210284Sjmallett
958210284Sjmallett    npei_ctl_status.u64 = cvmx_read_csr(CVMX_PEXP_NPEI_CTL_STATUS);
959210284Sjmallett    if (npei_ctl_status.s.host_mode)
960210284Sjmallett        return -1;
961210284Sjmallett
962210284Sjmallett    /* Enable bus master and memory */
963210284Sjmallett    cvmx_pcie_cfgx_write(pcie_port, CVMX_PCIEEP_CFG001, 0x6);
964210284Sjmallett
965210284Sjmallett    /* Max Payload Size (PCIE*_CFG030[MPS]) */
966210284Sjmallett    /* Max Read Request Size (PCIE*_CFG030[MRRS]) */
967210284Sjmallett    /* Relaxed-order, no-snoop enables (PCIE*_CFG030[RO_EN,NS_EN] */
968210284Sjmallett    /* Error Message Enables (PCIE*_CFG030[CE_EN,NFE_EN,FE_EN,UR_EN]) */
969210284Sjmallett    {
970210284Sjmallett        cvmx_pciercx_cfg030_t pciercx_cfg030;
971210284Sjmallett        pciercx_cfg030.u32 = cvmx_pcie_cfgx_read(pcie_port, CVMX_PCIERCX_CFG030(pcie_port));
972210284Sjmallett        pciercx_cfg030.s.mps = 0; /* Max payload size = 128 bytes (Limit of most PCs) */
973210284Sjmallett        pciercx_cfg030.s.mrrs = 0; /* Max read request size = 128 bytes for best Octeon DMA performance */
974210284Sjmallett        pciercx_cfg030.s.ro_en = 1; /* Enable relaxed ordering. */
975210284Sjmallett        pciercx_cfg030.s.ns_en = 1; /* Enable no snoop. */
976210284Sjmallett        pciercx_cfg030.s.ce_en = 1; /* Correctable error reporting enable. */
977210284Sjmallett        pciercx_cfg030.s.nfe_en = 1; /* Non-fatal error reporting enable. */
978210284Sjmallett        pciercx_cfg030.s.fe_en = 1; /* Fatal error reporting enable. */
979210284Sjmallett        pciercx_cfg030.s.ur_en = 1; /* Unsupported request reporting enable. */
980210284Sjmallett        cvmx_pcie_cfgx_write(pcie_port, CVMX_PCIERCX_CFG030(pcie_port), pciercx_cfg030.u32);
981210284Sjmallett    }
982210284Sjmallett
983210284Sjmallett    /* Max Payload Size (NPEI_CTL_STATUS2[MPS]) must match PCIE*_CFG030[MPS] */
984210284Sjmallett    /* Max Read Request Size (NPEI_CTL_STATUS2[MRRS]) must not exceed PCIE*_CFG030[MRRS] */
985210284Sjmallett    {
986210284Sjmallett        cvmx_npei_ctl_status2_t npei_ctl_status2;
987210284Sjmallett        npei_ctl_status2.u64 = cvmx_read_csr(CVMX_PEXP_NPEI_CTL_STATUS2);
988210284Sjmallett        npei_ctl_status2.s.mps = 0; /* Max payload size = 128 bytes (Limit of most PCs) */
989210284Sjmallett        npei_ctl_status2.s.mrrs = 0; /* Max read request size = 128 bytes for best Octeon DMA performance */
990210284Sjmallett        cvmx_write_csr(CVMX_PEXP_NPEI_CTL_STATUS2, npei_ctl_status2.u64);
991210284Sjmallett    }
992210284Sjmallett
993210284Sjmallett    /* Setup Mem access SubDID 12 to access Host memory */
994210284Sjmallett    {
995210284Sjmallett        cvmx_npei_mem_access_subidx_t mem_access_subid;
996210284Sjmallett        mem_access_subid.u64 = 0;
997210284Sjmallett        mem_access_subid.s.port = pcie_port; /* Port the request is sent to. */
998210284Sjmallett        mem_access_subid.s.nmerge = 1;  /* Merging is allowed in this window. */
999210284Sjmallett        mem_access_subid.s.esr = 0;     /* Endian-swap for Reads. */
1000210284Sjmallett        mem_access_subid.s.esw = 0;     /* Endian-swap for Writes. */
1001210284Sjmallett        mem_access_subid.s.nsr = 0;     /* Enable Snooping for Reads. Octeon doesn't care, but devices might want this more conservative setting */
1002210284Sjmallett        mem_access_subid.s.nsw = 0;     /* Enable Snoop for Writes. */
1003210284Sjmallett        mem_access_subid.s.ror = 0;     /* Disable Relaxed Ordering for Reads. */
1004210284Sjmallett        mem_access_subid.s.row = 0;     /* Disable Relaxed Ordering for Writes. */
1005210284Sjmallett        mem_access_subid.s.ba = 0;      /* PCIe Adddress Bits <63:34>. */
1006210284Sjmallett        cvmx_write_csr(CVMX_PEXP_NPEI_MEM_ACCESS_SUBIDX(12), mem_access_subid.u64);
1007210284Sjmallett    }
1008210284Sjmallett    return 0;
1009210284Sjmallett}
1010210284Sjmallett
1011210284Sjmallett
1012210284Sjmallett/**
1013210284Sjmallett * Wait for posted PCIe read/writes to reach the other side of
1014210284Sjmallett * the internal PCIe switch. This will insure that core
1015210284Sjmallett * read/writes are posted before anything after this function
1016210284Sjmallett * is called. This may be necessary when writing to memory that
1017210284Sjmallett * will later be read using the DMA/PKT engines.
1018210284Sjmallett *
1019210284Sjmallett * @param pcie_port PCIe port to wait for
1020210284Sjmallett */
1021210284Sjmallettvoid cvmx_pcie_wait_for_pending(int pcie_port)
1022210284Sjmallett{
1023210284Sjmallett    cvmx_npei_data_out_cnt_t npei_data_out_cnt;
1024210284Sjmallett    int a;
1025210284Sjmallett    int b;
1026210284Sjmallett    int c;
1027210284Sjmallett
1028210284Sjmallett    /* See section 9.8, PCIe Core-initiated Requests, in the manual for a
1029210284Sjmallett        description of how this code works */
1030210284Sjmallett    npei_data_out_cnt.u64 = cvmx_read_csr(CVMX_PEXP_NPEI_DATA_OUT_CNT);
1031210284Sjmallett    if (pcie_port)
1032210284Sjmallett    {
1033210284Sjmallett        if (!npei_data_out_cnt.s.p1_fcnt)
1034210284Sjmallett            return;
1035210284Sjmallett        a = npei_data_out_cnt.s.p1_ucnt;
1036210284Sjmallett        b = (a + npei_data_out_cnt.s.p1_fcnt-1) & 0xffff;
1037210284Sjmallett    }
1038210284Sjmallett    else
1039210284Sjmallett    {
1040210284Sjmallett        if (!npei_data_out_cnt.s.p0_fcnt)
1041210284Sjmallett            return;
1042210284Sjmallett        a = npei_data_out_cnt.s.p0_ucnt;
1043210284Sjmallett        b = (a + npei_data_out_cnt.s.p0_fcnt-1) & 0xffff;
1044210284Sjmallett    }
1045210284Sjmallett
1046210284Sjmallett    while (1)
1047210284Sjmallett    {
1048210284Sjmallett        npei_data_out_cnt.u64 = cvmx_read_csr(CVMX_PEXP_NPEI_DATA_OUT_CNT);
1049210284Sjmallett        c = (pcie_port) ? npei_data_out_cnt.s.p1_ucnt : npei_data_out_cnt.s.p0_ucnt;
1050210284Sjmallett        if (a<=b)
1051210284Sjmallett        {
1052210284Sjmallett            if ((c<a) || (c>b))
1053210284Sjmallett                return;
1054210284Sjmallett        }
1055210284Sjmallett        else
1056210284Sjmallett        {
1057210284Sjmallett            if ((c>b) && (c<a))
1058210284Sjmallett                return;
1059210284Sjmallett        }
1060210284Sjmallett    }
1061210284Sjmallett}
1062210284Sjmallett
1063