mv_pci.c revision 240489
1185089Sraj/*-
2209131Sraj * Copyright (c) 2008 MARVELL INTERNATIONAL LTD.
3209131Sraj * Copyright (c) 2010 The FreeBSD Foundation
4240489Sgber * Copyright (c) 2010-2012 Semihalf
5185089Sraj * All rights reserved.
6185089Sraj *
7185089Sraj * Developed by Semihalf.
8185089Sraj *
9209131Sraj * Portions of this software were developed by Semihalf
10209131Sraj * under sponsorship from the FreeBSD Foundation.
11209131Sraj *
12185089Sraj * Redistribution and use in source and binary forms, with or without
13185089Sraj * modification, are permitted provided that the following conditions
14185089Sraj * are met:
15185089Sraj * 1. Redistributions of source code must retain the above copyright
16185089Sraj *    notice, this list of conditions and the following disclaimer.
17185089Sraj * 2. Redistributions in binary form must reproduce the above copyright
18185089Sraj *    notice, this list of conditions and the following disclaimer in the
19185089Sraj *    documentation and/or other materials provided with the distribution.
20185089Sraj * 3. Neither the name of MARVELL nor the names of contributors
21185089Sraj *    may be used to endorse or promote products derived from this software
22185089Sraj *    without specific prior written permission.
23185089Sraj *
24185089Sraj * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
25185089Sraj * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26185089Sraj * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27185089Sraj * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
28185089Sraj * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29185089Sraj * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30185089Sraj * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31185089Sraj * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32185089Sraj * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33185089Sraj * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34185089Sraj * SUCH DAMAGE.
35185089Sraj */
36185089Sraj
37185089Sraj/*
38185089Sraj * Marvell integrated PCI/PCI-Express controller driver.
39185089Sraj */
40185089Sraj
41185089Sraj#include <sys/cdefs.h>
42185089Sraj__FBSDID("$FreeBSD: head/sys/arm/mv/mv_pci.c 240489 2012-09-14 09:57:41Z gber $");
43185089Sraj
44185089Sraj#include <sys/param.h>
45185089Sraj#include <sys/systm.h>
46185089Sraj#include <sys/kernel.h>
47185089Sraj#include <sys/lock.h>
48185089Sraj#include <sys/malloc.h>
49185089Sraj#include <sys/module.h>
50185089Sraj#include <sys/mutex.h>
51185089Sraj#include <sys/queue.h>
52185089Sraj#include <sys/bus.h>
53185089Sraj#include <sys/rman.h>
54185089Sraj#include <sys/endian.h>
55185089Sraj
56185089Sraj#include <vm/vm.h>
57185089Sraj#include <vm/pmap.h>
58185089Sraj
59209131Sraj#include <dev/fdt/fdt_common.h>
60209131Sraj#include <dev/ofw/ofw_bus.h>
61209131Sraj#include <dev/ofw/ofw_bus_subr.h>
62185089Sraj#include <dev/pci/pcivar.h>
63185089Sraj#include <dev/pci/pcireg.h>
64185089Sraj#include <dev/pci/pcib_private.h>
65185089Sraj
66209131Sraj#include "ofw_bus_if.h"
67185089Sraj#include "pcib_if.h"
68185089Sraj
69185089Sraj#include <machine/resource.h>
70185089Sraj#include <machine/bus.h>
71185089Sraj
72185089Sraj#include <arm/mv/mvreg.h>
73185089Sraj#include <arm/mv/mvvar.h>
74209131Sraj#include <arm/mv/mvwin.h>
75185089Sraj
76185089Sraj#define PCI_CFG_ENA		(1 << 31)
77185089Sraj#define PCI_CFG_BUS(bus)	(((bus) & 0xff) << 16)
78185089Sraj#define PCI_CFG_DEV(dev)	(((dev) & 0x1f) << 11)
79185089Sraj#define PCI_CFG_FUN(fun)	(((fun) & 0x7) << 8)
80185089Sraj#define PCI_CFG_PCIE_REG(reg)	((reg) & 0xfc)
81185089Sraj
82185089Sraj#define PCI_REG_CFG_ADDR	0x0C78
83185089Sraj#define PCI_REG_CFG_DATA	0x0C7C
84185089Sraj#define PCI_REG_P2P_CONF	0x1D14
85185089Sraj
86185089Sraj#define PCIE_REG_CFG_ADDR	0x18F8
87185089Sraj#define PCIE_REG_CFG_DATA	0x18FC
88185089Sraj#define PCIE_REG_CONTROL	0x1A00
89185089Sraj#define   PCIE_CTRL_LINK1X	0x00000001
90185089Sraj#define PCIE_REG_STATUS		0x1A04
91185089Sraj#define PCIE_REG_IRQ_MASK	0x1910
92185089Sraj
93240489Sgber#define PCIE_CONTROL_ROOT_CMPLX	(1 << 1)
94240489Sgber#define PCIE_CONTROL_HOT_RESET	(1 << 24)
95185089Sraj
96240489Sgber#define PCIE_LINK_TIMEOUT	1000000
97185089Sraj
98240489Sgber#define PCIE_STATUS_LINK_DOWN	1
99240489Sgber#define PCIE_STATUS_DEV_OFFS	16
100185089Sraj
101240489Sgber/* Minimum PCI Memory and I/O allocations taken from PCI spec (in bytes) */
102240489Sgber#define PCI_MIN_IO_ALLOC	4
103240489Sgber#define PCI_MIN_MEM_ALLOC	16
104240489Sgber
105240489Sgber#define BITS_PER_UINT32		(NBBY * sizeof(uint32_t))
106240489Sgber
107209131Srajstruct mv_pcib_softc {
108185089Sraj	device_t	sc_dev;
109185089Sraj
110209131Sraj	struct rman	sc_mem_rman;
111209131Sraj	bus_addr_t	sc_mem_base;
112209131Sraj	bus_addr_t	sc_mem_size;
113240489Sgber	uint32_t	sc_mem_map[MV_PCI_MEM_SLICE_SIZE /
114240489Sgber	    (PCI_MIN_MEM_ALLOC * BITS_PER_UINT32)];
115240489Sgber	int		sc_win_target;
116209131Sraj	int		sc_mem_win_attr;
117185089Sraj
118209131Sraj	struct rman	sc_io_rman;
119209131Sraj	bus_addr_t	sc_io_base;
120209131Sraj	bus_addr_t	sc_io_size;
121240489Sgber	uint32_t	sc_io_map[MV_PCI_IO_SLICE_SIZE /
122240489Sgber	    (PCI_MIN_IO_ALLOC * BITS_PER_UINT32)];
123209131Sraj	int		sc_io_win_attr;
124185089Sraj
125185089Sraj	struct resource	*sc_res;
126185089Sraj	bus_space_handle_t sc_bsh;
127185089Sraj	bus_space_tag_t	sc_bst;
128185089Sraj	int		sc_rid;
129185089Sraj
130185089Sraj	int		sc_busnr;		/* Host bridge bus number */
131185089Sraj	int		sc_devnr;		/* Host bridge device number */
132209131Sraj	int		sc_type;
133240489Sgber	int		sc_mode;		/* Endpoint / Root Complex */
134185089Sraj
135209131Sraj	struct fdt_pci_intr	sc_intr_info;
136185089Sraj};
137185089Sraj
138209131Sraj/* Local forward prototypes */
139209131Srajstatic int mv_pcib_decode_win(phandle_t, struct mv_pcib_softc *);
140209131Srajstatic void mv_pcib_hw_cfginit(void);
141209131Srajstatic uint32_t mv_pcib_hw_cfgread(struct mv_pcib_softc *, u_int, u_int,
142209131Sraj    u_int, u_int, int);
143209131Srajstatic void mv_pcib_hw_cfgwrite(struct mv_pcib_softc *, u_int, u_int,
144209131Sraj    u_int, u_int, uint32_t, int);
145209131Srajstatic int mv_pcib_init(struct mv_pcib_softc *, int, int);
146209131Srajstatic int mv_pcib_init_all_bars(struct mv_pcib_softc *, int, int, int, int);
147209131Srajstatic void mv_pcib_init_bridge(struct mv_pcib_softc *, int, int, int);
148209131Srajstatic int mv_pcib_intr_info(phandle_t, struct mv_pcib_softc *);
149209131Srajstatic inline void pcib_write_irq_mask(struct mv_pcib_softc *, uint32_t);
150240489Sgberstatic void mv_pcib_enable(struct mv_pcib_softc *, uint32_t);
151240489Sgberstatic int mv_pcib_mem_init(struct mv_pcib_softc *);
152185089Sraj
153209131Sraj/* Forward prototypes */
154209131Srajstatic int mv_pcib_probe(device_t);
155209131Srajstatic int mv_pcib_attach(device_t);
156209131Sraj
157209131Srajstatic struct resource *mv_pcib_alloc_resource(device_t, device_t, int, int *,
158185089Sraj    u_long, u_long, u_long, u_int);
159209131Srajstatic int mv_pcib_release_resource(device_t, device_t, int, int,
160185089Sraj    struct resource *);
161209131Srajstatic int mv_pcib_read_ivar(device_t, device_t, int, uintptr_t *);
162209131Srajstatic int mv_pcib_write_ivar(device_t, device_t, int, uintptr_t);
163185089Sraj
164209131Srajstatic int mv_pcib_maxslots(device_t);
165209131Srajstatic uint32_t mv_pcib_read_config(device_t, u_int, u_int, u_int, u_int, int);
166209131Srajstatic void mv_pcib_write_config(device_t, u_int, u_int, u_int, u_int,
167185089Sraj    uint32_t, int);
168209131Srajstatic int mv_pcib_route_interrupt(device_t, device_t, int);
169185089Sraj
170185089Sraj/*
171185089Sraj * Bus interface definitions.
172185089Sraj */
173209131Srajstatic device_method_t mv_pcib_methods[] = {
174185089Sraj	/* Device interface */
175209131Sraj	DEVMETHOD(device_probe,			mv_pcib_probe),
176209131Sraj	DEVMETHOD(device_attach,		mv_pcib_attach),
177185089Sraj
178185089Sraj	/* Bus interface */
179209131Sraj	DEVMETHOD(bus_read_ivar,		mv_pcib_read_ivar),
180209131Sraj	DEVMETHOD(bus_write_ivar,		mv_pcib_write_ivar),
181209131Sraj	DEVMETHOD(bus_alloc_resource,		mv_pcib_alloc_resource),
182209131Sraj	DEVMETHOD(bus_release_resource,		mv_pcib_release_resource),
183185089Sraj	DEVMETHOD(bus_activate_resource,	bus_generic_activate_resource),
184185089Sraj	DEVMETHOD(bus_deactivate_resource,	bus_generic_deactivate_resource),
185185089Sraj	DEVMETHOD(bus_setup_intr,		bus_generic_setup_intr),
186185089Sraj	DEVMETHOD(bus_teardown_intr,		bus_generic_teardown_intr),
187185089Sraj
188185089Sraj	/* pcib interface */
189209131Sraj	DEVMETHOD(pcib_maxslots,		mv_pcib_maxslots),
190209131Sraj	DEVMETHOD(pcib_read_config,		mv_pcib_read_config),
191209131Sraj	DEVMETHOD(pcib_write_config,		mv_pcib_write_config),
192209131Sraj	DEVMETHOD(pcib_route_interrupt,		mv_pcib_route_interrupt),
193240489Sgber
194209131Sraj	/* OFW bus interface */
195209131Sraj	DEVMETHOD(ofw_bus_get_compat,   ofw_bus_gen_get_compat),
196209131Sraj	DEVMETHOD(ofw_bus_get_model,    ofw_bus_gen_get_model),
197209131Sraj	DEVMETHOD(ofw_bus_get_name,     ofw_bus_gen_get_name),
198209131Sraj	DEVMETHOD(ofw_bus_get_node,     ofw_bus_gen_get_node),
199209131Sraj	DEVMETHOD(ofw_bus_get_type,     ofw_bus_gen_get_type),
200209131Sraj
201227843Smarius	DEVMETHOD_END
202185089Sraj};
203185089Sraj
204209131Srajstatic driver_t mv_pcib_driver = {
205185089Sraj	"pcib",
206209131Sraj	mv_pcib_methods,
207209131Sraj	sizeof(struct mv_pcib_softc),
208185089Sraj};
209185089Sraj
210185089Srajdevclass_t pcib_devclass;
211185089Sraj
212209131SrajDRIVER_MODULE(pcib, fdtbus, mv_pcib_driver, pcib_devclass, 0, 0);
213185089Sraj
214185089Srajstatic struct mtx pcicfg_mtx;
215185089Sraj
216185089Srajstatic int
217209131Srajmv_pcib_probe(device_t self)
218185089Sraj{
219218228Smarcel	phandle_t node;
220185089Sraj
221218228Smarcel	node = ofw_bus_get_node(self);
222218228Smarcel	if (!fdt_is_type(node, "pci"))
223209131Sraj		return (ENXIO);
224218228Smarcel
225218228Smarcel	if (!(fdt_is_compatible(node, "mrvl,pcie") ||
226218228Smarcel	    fdt_is_compatible(node, "mrvl,pci")))
227209131Sraj		return (ENXIO);
228185089Sraj
229209131Sraj	device_set_desc(self, "Marvell Integrated PCI/PCI-E Controller");
230209131Sraj	return (BUS_PROBE_DEFAULT);
231185089Sraj}
232185089Sraj
233185089Srajstatic int
234209131Srajmv_pcib_attach(device_t self)
235185089Sraj{
236209131Sraj	struct mv_pcib_softc *sc;
237209131Sraj	phandle_t node, parnode;
238240489Sgber	uint32_t val, unit;
239209131Sraj	int err;
240185089Sraj
241185089Sraj	sc = device_get_softc(self);
242209131Sraj	sc->sc_dev = self;
243240489Sgber	unit = fdt_get_unit(self);
244185089Sraj
245240489Sgber
246218228Smarcel	node = ofw_bus_get_node(self);
247218228Smarcel	parnode = OF_parent(node);
248218228Smarcel	if (fdt_is_compatible(node, "mrvl,pcie")) {
249209131Sraj		sc->sc_type = MV_TYPE_PCIE;
250240489Sgber		sc->sc_win_target = MV_WIN_PCIE_TARGET(unit);
251240489Sgber		sc->sc_mem_win_attr = MV_WIN_PCIE_MEM_ATTR(unit);
252240489Sgber		sc->sc_io_win_attr = MV_WIN_PCIE_IO_ATTR(unit);
253218228Smarcel	} else if (fdt_is_compatible(node, "mrvl,pci")) {
254209131Sraj		sc->sc_type = MV_TYPE_PCI;
255240489Sgber		sc->sc_win_target = MV_WIN_PCI_TARGET;
256209131Sraj		sc->sc_mem_win_attr = MV_WIN_PCI_MEM_ATTR;
257209131Sraj		sc->sc_io_win_attr = MV_WIN_PCI_IO_ATTR;
258209131Sraj	} else
259185089Sraj		return (ENXIO);
260185089Sraj
261209131Sraj	/*
262209131Sraj	 * Retrieve our mem-mapped registers range.
263209131Sraj	 */
264185089Sraj	sc->sc_rid = 0;
265185089Sraj	sc->sc_res = bus_alloc_resource_any(self, SYS_RES_MEMORY, &sc->sc_rid,
266185089Sraj	    RF_ACTIVE);
267185089Sraj	if (sc->sc_res == NULL) {
268209131Sraj		device_printf(self, "could not map memory\n");
269185089Sraj		return (ENXIO);
270185089Sraj	}
271185089Sraj	sc->sc_bst = rman_get_bustag(sc->sc_res);
272185089Sraj	sc->sc_bsh = rman_get_bushandle(sc->sc_res);
273185089Sraj
274240489Sgber	val = bus_space_read_4(sc->sc_bst, sc->sc_bsh, PCIE_REG_CONTROL);
275240489Sgber	sc->sc_mode = (val & PCIE_CONTROL_ROOT_CMPLX ? MV_MODE_ROOT :
276240489Sgber	    MV_MODE_ENDPOINT);
277240489Sgber
278209131Sraj	/*
279240489Sgber	 * Get PCI interrupt info.
280240489Sgber	 */
281240489Sgber	if ((sc->sc_mode == MV_MODE_ROOT) &&
282240489Sgber	    (mv_pcib_intr_info(node, sc) != 0)) {
283240489Sgber		device_printf(self, "could not retrieve interrupt info\n");
284240489Sgber		return (ENXIO);
285240489Sgber	}
286240489Sgber
287240489Sgber	/*
288209131Sraj	 * Configure decode windows for PCI(E) access.
289209131Sraj	 */
290209131Sraj	if (mv_pcib_decode_win(node, sc) != 0)
291209131Sraj		return (ENXIO);
292209131Sraj
293209131Sraj	mv_pcib_hw_cfginit();
294209131Sraj
295209131Sraj	/*
296240489Sgber	 * Enable PCIE device.
297209131Sraj	 */
298240489Sgber	mv_pcib_enable(sc, unit);
299185089Sraj
300240489Sgber	/*
301240489Sgber	 * Memory management.
302240489Sgber	 */
303240489Sgber	err = mv_pcib_mem_init(sc);
304240489Sgber	if (err)
305240489Sgber		return (err);
306185089Sraj
307240489Sgber	if (sc->sc_mode == MV_MODE_ROOT) {
308240489Sgber		err = mv_pcib_init(sc, sc->sc_busnr,
309240489Sgber		    mv_pcib_maxslots(sc->sc_dev));
310240489Sgber		if (err)
311240489Sgber			goto error;
312240489Sgber
313240489Sgber		device_add_child(self, "pci", -1);
314240489Sgber	} else {
315240489Sgber		sc->sc_devnr = 1;
316240489Sgber		bus_space_write_4(sc->sc_bst, sc->sc_bsh,
317240489Sgber		    PCIE_REG_STATUS, 1 << PCIE_STATUS_DEV_OFFS);
318240489Sgber		device_add_child(self, "pci_ep", -1);
319240489Sgber	}
320240489Sgber
321240489Sgber	return (bus_generic_attach(self));
322240489Sgber
323240489Sgbererror:
324240489Sgber	/* XXX SYS_RES_ should be released here */
325240489Sgber	rman_fini(&sc->sc_mem_rman);
326240489Sgber	rman_fini(&sc->sc_io_rman);
327240489Sgber
328240489Sgber	return (err);
329240489Sgber}
330240489Sgber
331240489Sgberstatic void
332240489Sgbermv_pcib_enable(struct mv_pcib_softc *sc, uint32_t unit)
333240489Sgber{
334240489Sgber	uint32_t val;
335240489Sgber#if !defined(SOC_MV_ARMADAXP)
336240489Sgber	int timeout;
337240489Sgber
338240489Sgber	/*
339240489Sgber	 * Check if PCIE device is enabled.
340240489Sgber	 */
341240489Sgber	if (read_cpu_ctrl(CPU_CONTROL) & CPU_CONTROL_PCIE_DISABLE(unit)) {
342240489Sgber		write_cpu_ctrl(CPU_CONTROL, read_cpu_ctrl(CPU_CONTROL) &
343240489Sgber		    ~(CPU_CONTROL_PCIE_DISABLE(unit)));
344240489Sgber
345240489Sgber		timeout = PCIE_LINK_TIMEOUT;
346240489Sgber		val = bus_space_read_4(sc->sc_bst, sc->sc_bsh,
347240489Sgber		    PCIE_REG_STATUS);
348240489Sgber		while (((val & PCIE_STATUS_LINK_DOWN) == 1) && (timeout > 0)) {
349240489Sgber			DELAY(1000);
350240489Sgber			timeout -= 1000;
351240489Sgber			val = bus_space_read_4(sc->sc_bst, sc->sc_bsh,
352240489Sgber			    PCIE_REG_STATUS);
353240489Sgber		}
354240489Sgber	}
355240489Sgber#endif
356240489Sgber
357240489Sgber
358240489Sgber	if (sc->sc_mode == MV_MODE_ROOT) {
359240489Sgber		/*
360240489Sgber		 * Enable PCI bridge.
361240489Sgber		 */
362240489Sgber		val = bus_space_read_4(sc->sc_bst, sc->sc_bsh, PCIR_COMMAND);
363240489Sgber		val |= PCIM_CMD_SERRESPEN | PCIM_CMD_BUSMASTEREN |
364240489Sgber		    PCIM_CMD_MEMEN | PCIM_CMD_PORTEN;
365240489Sgber		bus_space_write_4(sc->sc_bst, sc->sc_bsh, PCIR_COMMAND, val);
366240489Sgber	}
367240489Sgber}
368240489Sgber
369240489Sgberstatic int
370240489Sgbermv_pcib_mem_init(struct mv_pcib_softc *sc)
371240489Sgber{
372240489Sgber	int err;
373240489Sgber
374240489Sgber	/*
375240489Sgber	 * Memory management.
376240489Sgber	 */
377209131Sraj	sc->sc_mem_rman.rm_type = RMAN_ARRAY;
378209131Sraj	err = rman_init(&sc->sc_mem_rman);
379186932Sraj	if (err)
380186932Sraj		return (err);
381186932Sraj
382209131Sraj	sc->sc_io_rman.rm_type = RMAN_ARRAY;
383209131Sraj	err = rman_init(&sc->sc_io_rman);
384186932Sraj	if (err) {
385209131Sraj		rman_fini(&sc->sc_mem_rman);
386186932Sraj		return (err);
387186932Sraj	}
388186932Sraj
389209131Sraj	err = rman_manage_region(&sc->sc_mem_rman, sc->sc_mem_base,
390209131Sraj	    sc->sc_mem_base + sc->sc_mem_size - 1);
391186932Sraj	if (err)
392186932Sraj		goto error;
393186932Sraj
394209131Sraj	err = rman_manage_region(&sc->sc_io_rman, sc->sc_io_base,
395209131Sraj	    sc->sc_io_base + sc->sc_io_size - 1);
396186932Sraj	if (err)
397186932Sraj		goto error;
398186932Sraj
399240489Sgber	return (0);
400185089Sraj
401186932Srajerror:
402209131Sraj	rman_fini(&sc->sc_mem_rman);
403209131Sraj	rman_fini(&sc->sc_io_rman);
404240489Sgber
405186932Sraj	return (err);
406185089Sraj}
407185089Sraj
408240489Sgberstatic inline uint32_t
409240489Sgberpcib_bit_get(uint32_t *map, uint32_t bit)
410240489Sgber{
411240489Sgber	uint32_t n = bit / BITS_PER_UINT32;
412240489Sgber
413240489Sgber	bit = bit % BITS_PER_UINT32;
414240489Sgber	return (map[n] & (1 << bit));
415240489Sgber}
416240489Sgber
417240489Sgberstatic inline void
418240489Sgberpcib_bit_set(uint32_t *map, uint32_t bit)
419240489Sgber{
420240489Sgber	uint32_t n = bit / BITS_PER_UINT32;
421240489Sgber
422240489Sgber	bit = bit % BITS_PER_UINT32;
423240489Sgber	map[n] |= (1 << bit);
424240489Sgber}
425240489Sgber
426240489Sgberstatic inline uint32_t
427240489Sgberpcib_map_check(uint32_t *map, uint32_t start, uint32_t bits)
428240489Sgber{
429240489Sgber	uint32_t i;
430240489Sgber
431240489Sgber	for (i = start; i < start + bits; i++)
432240489Sgber		if (pcib_bit_get(map, i))
433240489Sgber			return (0);
434240489Sgber
435240489Sgber	return (1);
436240489Sgber}
437240489Sgber
438240489Sgberstatic inline void
439240489Sgberpcib_map_set(uint32_t *map, uint32_t start, uint32_t bits)
440240489Sgber{
441240489Sgber	uint32_t i;
442240489Sgber
443240489Sgber	for (i = start; i < start + bits; i++)
444240489Sgber		pcib_bit_set(map, i);
445240489Sgber}
446240489Sgber
447240489Sgber/*
448240489Sgber * The idea of this allocator is taken from ARM No-Cache memory
449240489Sgber * management code (sys/arm/arm/vm_machdep.c).
450240489Sgber */
451240489Sgberstatic bus_addr_t
452240489Sgberpcib_alloc(struct mv_pcib_softc *sc, uint32_t smask)
453240489Sgber{
454240489Sgber	uint32_t bits, bits_limit, i, *map, min_alloc, size;
455240489Sgber	bus_addr_t addr = 0;
456240489Sgber	bus_addr_t base;
457240489Sgber
458240489Sgber	if (smask & 1) {
459240489Sgber		base = sc->sc_io_base;
460240489Sgber		min_alloc = PCI_MIN_IO_ALLOC;
461240489Sgber		bits_limit = sc->sc_io_size / min_alloc;
462240489Sgber		map = sc->sc_io_map;
463240489Sgber		smask &= ~0x3;
464240489Sgber	} else {
465240489Sgber		base = sc->sc_mem_base;
466240489Sgber		min_alloc = PCI_MIN_MEM_ALLOC;
467240489Sgber		bits_limit = sc->sc_mem_size / min_alloc;
468240489Sgber		map = sc->sc_mem_map;
469240489Sgber		smask &= ~0xF;
470240489Sgber	}
471240489Sgber
472240489Sgber	size = ~smask + 1;
473240489Sgber	bits = size / min_alloc;
474240489Sgber
475240489Sgber	for (i = 0; i + bits <= bits_limit; i += bits)
476240489Sgber		if (pcib_map_check(map, i, bits)) {
477240489Sgber			pcib_map_set(map, i, bits);
478240489Sgber			addr = base + (i * min_alloc);
479240489Sgber			return (addr);
480240489Sgber		}
481240489Sgber
482240489Sgber	return (addr);
483240489Sgber}
484240489Sgber
485185089Srajstatic int
486209131Srajmv_pcib_init_bar(struct mv_pcib_softc *sc, int bus, int slot, int func,
487185089Sraj    int barno)
488185089Sraj{
489240489Sgber	uint32_t addr, bar;
490185089Sraj	int reg, width;
491185089Sraj
492185089Sraj	reg = PCIR_BAR(barno);
493240489Sgber
494240489Sgber	/*
495240489Sgber	 * Need to init the BAR register with 0xffffffff before correct
496240489Sgber	 * value can be read.
497240489Sgber	 */
498240489Sgber	mv_pcib_write_config(sc->sc_dev, bus, slot, func, reg, ~0, 4);
499209131Sraj	bar = mv_pcib_read_config(sc->sc_dev, bus, slot, func, reg, 4);
500185089Sraj	if (bar == 0)
501185089Sraj		return (1);
502185089Sraj
503185089Sraj	/* Calculate BAR size: 64 or 32 bit (in 32-bit units) */
504185089Sraj	width = ((bar & 7) == 4) ? 2 : 1;
505185089Sraj
506240489Sgber	addr = pcib_alloc(sc, bar);
507240489Sgber	if (!addr)
508185089Sraj		return (-1);
509185089Sraj
510185089Sraj	if (bootverbose)
511240489Sgber		printf("PCI %u:%u:%u: reg %x: smask=%08x: addr=%08x\n",
512240489Sgber		    bus, slot, func, reg, bar, addr);
513185089Sraj
514209131Sraj	mv_pcib_write_config(sc->sc_dev, bus, slot, func, reg, addr, 4);
515185089Sraj	if (width == 2)
516209131Sraj		mv_pcib_write_config(sc->sc_dev, bus, slot, func, reg + 4,
517185089Sraj		    0, 4);
518185089Sraj
519185089Sraj	return (width);
520185089Sraj}
521185089Sraj
522185089Srajstatic void
523209131Srajmv_pcib_init_bridge(struct mv_pcib_softc *sc, int bus, int slot, int func)
524185089Sraj{
525185089Sraj	bus_addr_t io_base, mem_base;
526185089Sraj	uint32_t io_limit, mem_limit;
527185089Sraj	int secbus;
528185089Sraj
529209131Sraj	io_base = sc->sc_io_base;
530209131Sraj	io_limit = io_base + sc->sc_io_size - 1;
531209131Sraj	mem_base = sc->sc_mem_base;
532209131Sraj	mem_limit = mem_base + sc->sc_mem_size - 1;
533185089Sraj
534185089Sraj	/* Configure I/O decode registers */
535209131Sraj	mv_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_IOBASEL_1,
536185639Sraj	    io_base >> 8, 1);
537209131Sraj	mv_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_IOBASEH_1,
538185639Sraj	    io_base >> 16, 2);
539209131Sraj	mv_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_IOLIMITL_1,
540185089Sraj	    io_limit >> 8, 1);
541209131Sraj	mv_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_IOLIMITH_1,
542185089Sraj	    io_limit >> 16, 2);
543185089Sraj
544185089Sraj	/* Configure memory decode registers */
545209131Sraj	mv_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_MEMBASE_1,
546185089Sraj	    mem_base >> 16, 2);
547209131Sraj	mv_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_MEMLIMIT_1,
548185089Sraj	    mem_limit >> 16, 2);
549185089Sraj
550185089Sraj	/* Disable memory prefetch decode */
551209131Sraj	mv_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_PMBASEL_1,
552185089Sraj	    0x10, 2);
553209131Sraj	mv_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_PMBASEH_1,
554185089Sraj	    0x0, 4);
555209131Sraj	mv_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_PMLIMITL_1,
556185089Sraj	    0xF, 2);
557209131Sraj	mv_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_PMLIMITH_1,
558185089Sraj	    0x0, 4);
559185089Sraj
560209131Sraj	secbus = mv_pcib_read_config(sc->sc_dev, bus, slot, func,
561185089Sraj	    PCIR_SECBUS_1, 1);
562185089Sraj
563185089Sraj	/* Configure buses behind the bridge */
564209131Sraj	mv_pcib_init(sc, secbus, PCI_SLOTMAX);
565185089Sraj}
566185089Sraj
567185089Srajstatic int
568209131Srajmv_pcib_init(struct mv_pcib_softc *sc, int bus, int maxslot)
569185089Sraj{
570185089Sraj	int slot, func, maxfunc, error;
571185089Sraj	uint8_t hdrtype, command, class, subclass;
572185089Sraj
573185089Sraj	for (slot = 0; slot <= maxslot; slot++) {
574185089Sraj		maxfunc = 0;
575185089Sraj		for (func = 0; func <= maxfunc; func++) {
576209131Sraj			hdrtype = mv_pcib_read_config(sc->sc_dev, bus, slot,
577185089Sraj			    func, PCIR_HDRTYPE, 1);
578185089Sraj
579185089Sraj			if ((hdrtype & PCIM_HDRTYPE) > PCI_MAXHDRTYPE)
580185089Sraj				continue;
581185089Sraj
582185089Sraj			if (func == 0 && (hdrtype & PCIM_MFDEV))
583185089Sraj				maxfunc = PCI_FUNCMAX;
584185089Sraj
585209131Sraj			command = mv_pcib_read_config(sc->sc_dev, bus, slot,
586185089Sraj			    func, PCIR_COMMAND, 1);
587185089Sraj			command &= ~(PCIM_CMD_MEMEN | PCIM_CMD_PORTEN);
588209131Sraj			mv_pcib_write_config(sc->sc_dev, bus, slot, func,
589185089Sraj			    PCIR_COMMAND, command, 1);
590185089Sraj
591209131Sraj			error = mv_pcib_init_all_bars(sc, bus, slot, func,
592185089Sraj			    hdrtype);
593185089Sraj
594185089Sraj			if (error)
595185089Sraj				return (error);
596185089Sraj
597185089Sraj			command |= PCIM_CMD_BUSMASTEREN | PCIM_CMD_MEMEN |
598185089Sraj			    PCIM_CMD_PORTEN;
599209131Sraj			mv_pcib_write_config(sc->sc_dev, bus, slot, func,
600185089Sraj			    PCIR_COMMAND, command, 1);
601185089Sraj
602185089Sraj			/* Handle PCI-PCI bridges */
603209131Sraj			class = mv_pcib_read_config(sc->sc_dev, bus, slot,
604185089Sraj			    func, PCIR_CLASS, 1);
605209131Sraj			subclass = mv_pcib_read_config(sc->sc_dev, bus, slot,
606185089Sraj			    func, PCIR_SUBCLASS, 1);
607185089Sraj
608185089Sraj			if (class != PCIC_BRIDGE ||
609185089Sraj			    subclass != PCIS_BRIDGE_PCI)
610185089Sraj				continue;
611185089Sraj
612209131Sraj			mv_pcib_init_bridge(sc, bus, slot, func);
613185089Sraj		}
614185089Sraj	}
615185089Sraj
616185089Sraj	/* Enable all ABCD interrupts */
617185089Sraj	pcib_write_irq_mask(sc, (0xF << 24));
618185089Sraj
619185089Sraj	return (0);
620185089Sraj}
621185089Sraj
622209131Srajstatic int
623209131Srajmv_pcib_init_all_bars(struct mv_pcib_softc *sc, int bus, int slot,
624209131Sraj    int func, int hdrtype)
625209131Sraj{
626209131Sraj	int maxbar, bar, i;
627209131Sraj
628209131Sraj	maxbar = (hdrtype & PCIM_HDRTYPE) ? 0 : 6;
629209131Sraj	bar = 0;
630209131Sraj
631209131Sraj	/* Program the base address registers */
632209131Sraj	while (bar < maxbar) {
633209131Sraj		i = mv_pcib_init_bar(sc, bus, slot, func, bar);
634209131Sraj		bar += i;
635209131Sraj		if (i < 0) {
636209131Sraj			device_printf(sc->sc_dev,
637209131Sraj			    "PCI IO/Memory space exhausted\n");
638209131Sraj			return (ENOMEM);
639209131Sraj		}
640209131Sraj	}
641209131Sraj
642209131Sraj	return (0);
643209131Sraj}
644209131Sraj
645185089Srajstatic struct resource *
646209131Srajmv_pcib_alloc_resource(device_t dev, device_t child, int type, int *rid,
647185089Sraj    u_long start, u_long end, u_long count, u_int flags)
648185089Sraj{
649209131Sraj	struct mv_pcib_softc *sc = device_get_softc(dev);
650186932Sraj	struct rman *rm = NULL;
651186932Sraj	struct resource *res;
652185089Sraj
653186932Sraj	switch (type) {
654186932Sraj	case SYS_RES_IOPORT:
655209131Sraj		rm = &sc->sc_io_rman;
656186932Sraj		break;
657186932Sraj	case SYS_RES_MEMORY:
658209131Sraj		rm = &sc->sc_mem_rman;
659186932Sraj		break;
660186932Sraj	default:
661240489Sgber		return (BUS_ALLOC_RESOURCE(device_get_parent(dev), dev,
662186932Sraj		    type, rid, start, end, count, flags));
663186932Sraj	};
664186932Sraj
665240489Sgber	if ((start == 0UL) && (end == ~0UL)) {
666240489Sgber		start = sc->sc_mem_base;
667240489Sgber		end = sc->sc_mem_base + sc->sc_mem_size - 1;
668240489Sgber		count = sc->sc_mem_size;
669240489Sgber	}
670240489Sgber
671240489Sgber	if ((start < sc->sc_mem_base) || (start + count - 1 != end) ||
672240489Sgber	    (end > sc->sc_mem_base + sc->sc_mem_size - 1))
673240489Sgber		return (NULL);
674240489Sgber
675186932Sraj	res = rman_reserve_resource(rm, start, end, count, flags, child);
676186932Sraj	if (res == NULL)
677186932Sraj		return (NULL);
678186932Sraj
679186932Sraj	rman_set_rid(res, *rid);
680209131Sraj	rman_set_bustag(res, fdtbus_bs_tag);
681186932Sraj	rman_set_bushandle(res, start);
682186932Sraj
683186932Sraj	if (flags & RF_ACTIVE)
684186932Sraj		if (bus_activate_resource(child, type, *rid, res)) {
685186932Sraj			rman_release_resource(res);
686186932Sraj			return (NULL);
687186932Sraj		}
688186932Sraj
689186932Sraj	return (res);
690185089Sraj}
691185089Sraj
692185089Srajstatic int
693209131Srajmv_pcib_release_resource(device_t dev, device_t child, int type, int rid,
694185089Sraj    struct resource *res)
695185089Sraj{
696185089Sraj
697186932Sraj	if (type != SYS_RES_IOPORT && type != SYS_RES_MEMORY)
698186932Sraj		return (BUS_RELEASE_RESOURCE(device_get_parent(dev), child,
699186932Sraj		    type, rid, res));
700186932Sraj
701186932Sraj	return (rman_release_resource(res));
702185089Sraj}
703185089Sraj
704185089Srajstatic int
705209131Srajmv_pcib_read_ivar(device_t dev, device_t child, int which, uintptr_t *result)
706185089Sraj{
707209131Sraj	struct mv_pcib_softc *sc = device_get_softc(dev);
708185089Sraj
709185089Sraj	switch (which) {
710185089Sraj	case PCIB_IVAR_BUS:
711185089Sraj		*result = sc->sc_busnr;
712185089Sraj		return (0);
713185089Sraj	case PCIB_IVAR_DOMAIN:
714185089Sraj		*result = device_get_unit(dev);
715185089Sraj		return (0);
716185089Sraj	}
717185089Sraj
718185089Sraj	return (ENOENT);
719185089Sraj}
720185089Sraj
721185089Srajstatic int
722209131Srajmv_pcib_write_ivar(device_t dev, device_t child, int which, uintptr_t value)
723185089Sraj{
724209131Sraj	struct mv_pcib_softc *sc = device_get_softc(dev);
725185089Sraj
726185089Sraj	switch (which) {
727185089Sraj	case PCIB_IVAR_BUS:
728185089Sraj		sc->sc_busnr = value;
729185089Sraj		return (0);
730185089Sraj	}
731185089Sraj
732185089Sraj	return (ENOENT);
733185089Sraj}
734209131Sraj
735209131Srajstatic inline void
736209131Srajpcib_write_irq_mask(struct mv_pcib_softc *sc, uint32_t mask)
737209131Sraj{
738209131Sraj
739209131Sraj	if (!sc->sc_type != MV_TYPE_PCI)
740209131Sraj		return;
741209131Sraj
742209131Sraj	bus_space_write_4(sc->sc_bst, sc->sc_bsh, PCIE_REG_IRQ_MASK, mask);
743209131Sraj}
744209131Sraj
745209131Srajstatic void
746209131Srajmv_pcib_hw_cfginit(void)
747209131Sraj{
748209131Sraj	static int opened = 0;
749209131Sraj
750209131Sraj	if (opened)
751209131Sraj		return;
752209131Sraj
753209131Sraj	mtx_init(&pcicfg_mtx, "pcicfg", NULL, MTX_SPIN);
754209131Sraj	opened = 1;
755209131Sraj}
756209131Sraj
757209131Srajstatic uint32_t
758209131Srajmv_pcib_hw_cfgread(struct mv_pcib_softc *sc, u_int bus, u_int slot,
759209131Sraj    u_int func, u_int reg, int bytes)
760209131Sraj{
761209131Sraj	uint32_t addr, data, ca, cd;
762209131Sraj
763209131Sraj	ca = (sc->sc_type != MV_TYPE_PCI) ?
764209131Sraj	    PCIE_REG_CFG_ADDR : PCI_REG_CFG_ADDR;
765209131Sraj	cd = (sc->sc_type != MV_TYPE_PCI) ?
766209131Sraj	    PCIE_REG_CFG_DATA : PCI_REG_CFG_DATA;
767209131Sraj	addr = PCI_CFG_ENA | PCI_CFG_BUS(bus) | PCI_CFG_DEV(slot) |
768209131Sraj	    PCI_CFG_FUN(func) | PCI_CFG_PCIE_REG(reg);
769209131Sraj
770209131Sraj	mtx_lock_spin(&pcicfg_mtx);
771209131Sraj	bus_space_write_4(sc->sc_bst, sc->sc_bsh, ca, addr);
772209131Sraj
773209131Sraj	data = ~0;
774209131Sraj	switch (bytes) {
775209131Sraj	case 1:
776209131Sraj		data = bus_space_read_1(sc->sc_bst, sc->sc_bsh,
777209131Sraj		    cd + (reg & 3));
778209131Sraj		break;
779209131Sraj	case 2:
780209131Sraj		data = le16toh(bus_space_read_2(sc->sc_bst, sc->sc_bsh,
781209131Sraj		    cd + (reg & 2)));
782209131Sraj		break;
783209131Sraj	case 4:
784209131Sraj		data = le32toh(bus_space_read_4(sc->sc_bst, sc->sc_bsh,
785209131Sraj		    cd));
786209131Sraj		break;
787209131Sraj	}
788209131Sraj	mtx_unlock_spin(&pcicfg_mtx);
789209131Sraj	return (data);
790209131Sraj}
791209131Sraj
792209131Srajstatic void
793209131Srajmv_pcib_hw_cfgwrite(struct mv_pcib_softc *sc, u_int bus, u_int slot,
794209131Sraj    u_int func, u_int reg, uint32_t data, int bytes)
795209131Sraj{
796209131Sraj	uint32_t addr, ca, cd;
797209131Sraj
798209131Sraj	ca = (sc->sc_type != MV_TYPE_PCI) ?
799209131Sraj	    PCIE_REG_CFG_ADDR : PCI_REG_CFG_ADDR;
800209131Sraj	cd = (sc->sc_type != MV_TYPE_PCI) ?
801209131Sraj	    PCIE_REG_CFG_DATA : PCI_REG_CFG_DATA;
802209131Sraj	addr = PCI_CFG_ENA | PCI_CFG_BUS(bus) | PCI_CFG_DEV(slot) |
803209131Sraj	    PCI_CFG_FUN(func) | PCI_CFG_PCIE_REG(reg);
804209131Sraj
805209131Sraj	mtx_lock_spin(&pcicfg_mtx);
806209131Sraj	bus_space_write_4(sc->sc_bst, sc->sc_bsh, ca, addr);
807209131Sraj
808209131Sraj	switch (bytes) {
809209131Sraj	case 1:
810209131Sraj		bus_space_write_1(sc->sc_bst, sc->sc_bsh,
811209131Sraj		    cd + (reg & 3), data);
812209131Sraj		break;
813209131Sraj	case 2:
814209131Sraj		bus_space_write_2(sc->sc_bst, sc->sc_bsh,
815209131Sraj		    cd + (reg & 2), htole16(data));
816209131Sraj		break;
817209131Sraj	case 4:
818209131Sraj		bus_space_write_4(sc->sc_bst, sc->sc_bsh,
819209131Sraj		    cd, htole32(data));
820209131Sraj		break;
821209131Sraj	}
822209131Sraj	mtx_unlock_spin(&pcicfg_mtx);
823209131Sraj}
824209131Sraj
825209131Srajstatic int
826209131Srajmv_pcib_maxslots(device_t dev)
827209131Sraj{
828209131Sraj	struct mv_pcib_softc *sc = device_get_softc(dev);
829209131Sraj
830209131Sraj	return ((sc->sc_type != MV_TYPE_PCI) ? 1 : PCI_SLOTMAX);
831209131Sraj}
832209131Sraj
833209131Srajstatic uint32_t
834209131Srajmv_pcib_read_config(device_t dev, u_int bus, u_int slot, u_int func,
835209131Sraj    u_int reg, int bytes)
836209131Sraj{
837209131Sraj	struct mv_pcib_softc *sc = device_get_softc(dev);
838209131Sraj
839240489Sgber	/* Return ~0 if link is inactive or trying to read from Root */
840240489Sgber	if ((bus_space_read_4(sc->sc_bst, sc->sc_bsh, PCIE_REG_STATUS) &
841240489Sgber	    PCIE_STATUS_LINK_DOWN) || (slot == 0))
842209131Sraj		return (~0U);
843209131Sraj
844209131Sraj	return (mv_pcib_hw_cfgread(sc, bus, slot, func, reg, bytes));
845209131Sraj}
846209131Sraj
847209131Srajstatic void
848209131Srajmv_pcib_write_config(device_t dev, u_int bus, u_int slot, u_int func,
849209131Sraj    u_int reg, uint32_t val, int bytes)
850209131Sraj{
851209131Sraj	struct mv_pcib_softc *sc = device_get_softc(dev);
852209131Sraj
853240489Sgber	/* Return if link is inactive or trying to write to Root */
854240489Sgber	if ((bus_space_read_4(sc->sc_bst, sc->sc_bsh, PCIE_REG_STATUS) &
855240489Sgber	    PCIE_STATUS_LINK_DOWN) || (slot == 0))
856209131Sraj		return;
857209131Sraj
858209131Sraj	mv_pcib_hw_cfgwrite(sc, bus, slot, func, reg, val, bytes);
859209131Sraj}
860209131Sraj
861209131Srajstatic int
862209131Srajmv_pcib_route_interrupt(device_t pcib, device_t dev, int pin)
863209131Sraj{
864209131Sraj	struct mv_pcib_softc *sc;
865209131Sraj	int err, interrupt;
866209131Sraj
867209131Sraj	sc = device_get_softc(pcib);
868209131Sraj
869209131Sraj	err = fdt_pci_route_intr(pci_get_bus(dev), pci_get_slot(dev),
870209131Sraj	    pci_get_function(dev), pin, &sc->sc_intr_info, &interrupt);
871209131Sraj	if (err == 0)
872209131Sraj		return (interrupt);
873209131Sraj
874209131Sraj	device_printf(pcib, "could not route pin %d for device %d.%d\n",
875209131Sraj	    pin, pci_get_slot(dev), pci_get_function(dev));
876209131Sraj	return (PCI_INVALID_IRQ);
877209131Sraj}
878209131Sraj
879209131Srajstatic int
880209131Srajmv_pcib_decode_win(phandle_t node, struct mv_pcib_softc *sc)
881209131Sraj{
882209131Sraj	struct fdt_pci_range io_space, mem_space;
883209131Sraj	device_t dev;
884209131Sraj	int error;
885209131Sraj
886209131Sraj	dev = sc->sc_dev;
887209131Sraj
888209131Sraj	if ((error = fdt_pci_ranges(node, &io_space, &mem_space)) != 0) {
889209131Sraj		device_printf(dev, "could not retrieve 'ranges' data\n");
890209131Sraj		return (error);
891209131Sraj	}
892209131Sraj
893209131Sraj	/* Configure CPU decoding windows */
894240489Sgber	error = decode_win_cpu_set(sc->sc_win_target,
895240489Sgber	    sc->sc_io_win_attr, io_space.base_parent, io_space.len, ~0);
896209131Sraj	if (error < 0) {
897209131Sraj		device_printf(dev, "could not set up CPU decode "
898209131Sraj		    "window for PCI IO\n");
899209131Sraj		return (ENXIO);
900209131Sraj	}
901240489Sgber	error = decode_win_cpu_set(sc->sc_win_target,
902240489Sgber	    sc->sc_mem_win_attr, mem_space.base_parent, mem_space.len,
903240489Sgber	    mem_space.base_parent);
904209131Sraj	if (error < 0) {
905209131Sraj		device_printf(dev, "could not set up CPU decode "
906209131Sraj		    "windows for PCI MEM\n");
907209131Sraj		return (ENXIO);
908209131Sraj	}
909209131Sraj
910209131Sraj	sc->sc_io_base = io_space.base_parent;
911209131Sraj	sc->sc_io_size = io_space.len;
912209131Sraj
913209131Sraj	sc->sc_mem_base = mem_space.base_parent;
914209131Sraj	sc->sc_mem_size = mem_space.len;
915209131Sraj
916209131Sraj	return (0);
917209131Sraj}
918209131Sraj
919209131Srajstatic int
920209131Srajmv_pcib_intr_info(phandle_t node, struct mv_pcib_softc *sc)
921209131Sraj{
922209131Sraj	int error;
923209131Sraj
924209131Sraj	if ((error = fdt_pci_intr_info(node, &sc->sc_intr_info)) != 0)
925209131Sraj		return (error);
926209131Sraj
927209131Sraj	return (0);
928209131Sraj}
929209131Sraj
930