1/*-
2 * Copyright (c) 2007 Bruce M. Simpson.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27/*
28 * Child driver for PCI host bridge core.
29 */
30
31#include <sys/cdefs.h>
32__FBSDID("$FreeBSD$");
33
34#include <sys/param.h>
35#include <sys/systm.h>
36#include <sys/bus.h>
37#include <sys/kernel.h>
38#include <sys/module.h>
39#include <sys/rman.h>
40#include <sys/malloc.h>
41#include <sys/endian.h>
42
43#include <vm/vm.h>
44#include <vm/pmap.h>
45#include <vm/vm_extern.h>
46
47#include <machine/bus.h>
48#include <machine/cpu.h>
49#include <machine/pcb.h>
50#include <machine/pmap.h>
51
52#include <dev/pci/pcireg.h>
53#include <dev/pci/pcivar.h>
54#include <dev/pci/pcib_private.h>
55
56#include "pcib_if.h"
57
58#include <dev/siba/siba_ids.h>
59#include <dev/siba/sibareg.h>
60#include <dev/siba/sibavar.h>
61#include <dev/siba/siba_pcibvar.h>
62
63#ifndef MIPS_MEM_RID
64#define MIPS_MEM_RID 0x20
65#endif
66
67#define SBPCI_SLOTMAX 15
68
69#define SBPCI_READ_4(sc, reg)					\
70	bus_space_write_4((sc)->sc_bt, (sc)->sc_bh, (reg))
71
72#define SBPCI_WRITE_4(sc, reg, val)					\
73	bus_space_write_4((sc)->sc_bt, (sc)->sc_bh, (reg), (val))
74
75/*
76 * PCI Configuration space window (64MB).
77 * contained in SBTOPCI1 window.
78 */
79#define SBPCI_CFGBASE			0x0C000000
80#define SBPCI_CFGSIZE			0x01000000
81
82/*
83 * TODO: implement type 1 config space access (ie beyond bus 0)
84 * we may need to tweak the windows to do this
85 * TODO: interrupt routing.
86 * TODO: fully implement bus allocation.
87 * TODO: implement resource managers.
88 * TODO: code cleanup.
89 */
90
91static int	siba_pcib_activate_resource(device_t, device_t, int,
92		    int, struct resource *);
93static struct resource *
94		siba_pcib_alloc_resource(device_t, device_t, int, int *,
95		    u_long , u_long, u_long, u_int);
96static int	siba_pcib_attach(device_t);
97static int	siba_pcib_deactivate_resource(device_t, device_t, int,
98		    int, struct resource *);
99static int	siba_pcib_maxslots(device_t);
100static int	siba_pcib_probe(device_t);
101static u_int32_t
102		siba_pcib_read_config(device_t, u_int, u_int, u_int, u_int,
103		    int);
104static int	siba_pcib_read_ivar(device_t, device_t, int, uintptr_t *);
105static int	siba_pcib_release_resource(device_t, device_t, int, int,
106		    struct resource *);
107static int	siba_pcib_route_interrupt(device_t, device_t, int);
108static int	siba_pcib_setup_intr(device_t, device_t, struct resource *,
109		    int, driver_filter_t *, driver_intr_t *, void *, void **);
110static int	siba_pcib_teardown_intr(device_t, device_t, struct resource *,
111		    void *);
112static void	siba_pcib_write_config(device_t, u_int, u_int, u_int, u_int,
113		    u_int32_t, int);
114static int	siba_pcib_write_ivar(device_t, device_t, int, uintptr_t);
115
116static int
117siba_pcib_probe(device_t dev)
118{
119
120	/* TODO: support earlier cores. */
121	/* TODO: Check if PCI host mode is enabled in the SPROM. */
122	if (siba_get_vendor(dev) == SIBA_VID_BROADCOM &&
123	    siba_get_device(dev) == SIBA_DEVID_PCI) {
124		device_set_desc(dev, "SiBa-to-PCI host bridge");
125		return (BUS_PROBE_DEFAULT);
126	}
127
128	return (ENXIO);
129}
130
131//extern int rman_debug;
132
133static int
134siba_pcib_attach(device_t dev)
135{
136	struct siba_pcib_softc *sc = device_get_softc(dev);
137	int rid;
138
139	/*
140	 * Allocate the resources which the parent bus has already
141	 * determined for us.
142	 */
143	rid = MIPS_MEM_RID;	/* XXX */
144	//rman_debug = 1;
145	sc->sc_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
146	    RF_ACTIVE);
147	if (sc->sc_mem == NULL) {
148		device_printf(dev, "unable to allocate memory\n");
149		return (ENXIO);
150	}
151
152	sc->sc_bt = rman_get_bustag(sc->sc_mem);
153	sc->sc_bh = rman_get_bushandle(sc->sc_mem);
154
155	device_printf(dev, "bridge registers addr 0x%08x vaddr %p\n",
156	    (uint32_t)sc->sc_bh, rman_get_virtual(sc->sc_mem));
157
158	SBPCI_WRITE_4(sc, 0x0000, 0x05);
159	SBPCI_WRITE_4(sc, 0x0000, 0x0D);
160	DELAY(150);
161	SBPCI_WRITE_4(sc, 0x0000, 0x0F);
162	SBPCI_WRITE_4(sc, 0x0010, 0x01);
163	DELAY(1);
164
165	bus_space_handle_t sc_cfg_hand;
166	int error;
167
168	/*
169	 * XXX this doesn't actually do anything on mips; however... should
170	 * we not be mapping to KSEG1? we need to wire down the range.
171	 */
172	error = bus_space_map(sc->sc_bt, SBPCI_CFGBASE, SBPCI_CFGSIZE,
173	    0, &sc_cfg_hand);
174	if (error) {
175		device_printf(dev, "cannot map PCI configuration space\n");
176		return (ENXIO);
177	}
178	device_printf(dev, "mapped pci config space at 0x%08x\n",
179	    (uint32_t)sc_cfg_hand);
180
181	/*
182	 * Setup configuration, io, and dma space windows.
183	 * XXX we need to be able to do type 1 too.
184	 * we probably don't need to be able to do i/o cycles.
185	 */
186
187	/* I/O read/write window */
188	SBPCI_WRITE_4(sc, SIBA_PCICORE_SBTOPCI0, 1);
189	/* type 0 configuration only */
190	SBPCI_WRITE_4(sc, SIBA_PCICORE_SBTOPCI1, 2);
191	SBPCI_WRITE_4(sc, SIBA_PCICORE_SBTOPCI2, 1 << 30); /* memory only */
192	DELAY(500);
193
194	/* XXX resource managers */
195
196	device_add_child(dev, "pci", -1);
197	return (bus_generic_attach(dev));
198}
199
200/* bus functions */
201
202static int
203siba_pcib_read_ivar(device_t dev, device_t child, int which, uintptr_t *result)
204{
205	struct siba_pcib_softc *sc;
206
207	sc = device_get_softc(dev);
208	switch (which) {
209	case PCIB_IVAR_BUS:
210		*result = sc->sc_bus;
211		return (0);
212	}
213
214	return (ENOENT);
215}
216
217static int
218siba_pcib_write_ivar(device_t dev, device_t child, int which, uintptr_t value)
219{
220	struct siba_pcib_softc *sc;
221
222	sc = device_get_softc(dev);
223	switch (which) {
224	case PCIB_IVAR_BUS:
225		sc->sc_bus = value;
226		return (0);
227	}
228
229	return (ENOENT);
230}
231
232static int
233siba_pcib_setup_intr(device_t dev, device_t child, struct resource *ires,
234    int flags, driver_filter_t *filt, driver_intr_t *intr, void *arg,
235    void **cookiep)
236{
237
238	return (BUS_SETUP_INTR(device_get_parent(dev), child, ires, flags,
239	    filt, intr, arg, cookiep));
240}
241
242static int
243siba_pcib_teardown_intr(device_t dev, device_t child, struct resource *vec,
244     void *cookie)
245{
246
247	return (BUS_TEARDOWN_INTR(device_get_parent(dev), child, vec, cookie));
248}
249
250static struct resource *
251siba_pcib_alloc_resource(device_t bus, device_t child, int type, int *rid,
252    u_long start, u_long end, u_long count, u_int flags)
253{
254#if 1
255
256	//device_printf(bus, "%s: not yet implemented\n", __func__);
257	return (NULL);
258#else
259	bus_space_tag_t tag;
260	struct siba_pcib_softc *sc = device_get_softc(bus);
261	struct rman *rmanp;
262	struct resource *rv;
263
264	tag = 0;
265	rv = NULL;
266	switch (type) {
267	case SYS_RES_IRQ:
268		rmanp = &sc->sc_irq_rman;
269		break;
270
271	case SYS_RES_MEMORY:
272		rmanp = &sc->sc_mem_rman;
273		tag = &sc->sc_pci_memt;
274		break;
275
276	default:
277		return (rv);
278	}
279
280	rv = rman_reserve_resource(rmanp, start, end, count, flags, child);
281	if (rv != NULL) {
282		rman_set_rid(rv, *rid);
283		if (type == SYS_RES_MEMORY) {
284#if 0
285			rman_set_bustag(rv, tag);
286			rman_set_bushandle(rv, rman_get_bushandle(sc->sc_mem) +
287			    (rman_get_start(rv) - IXP425_PCI_MEM_HWBASE));
288#endif
289		}
290	}
291
292	return (rv);
293#endif
294}
295
296static int
297siba_pcib_activate_resource(device_t bus, device_t child, int type, int rid,
298    struct resource *r)
299{
300
301	device_printf(bus, "%s: not yet implemented\n", __func__);
302	device_printf(bus, "%s called activate_resource\n",
303	    device_get_nameunit(child));
304	return (ENXIO);
305}
306
307static int
308siba_pcib_deactivate_resource(device_t bus, device_t child, int type, int rid,
309    struct resource *r)
310{
311
312	device_printf(bus, "%s: not yet implemented\n", __func__);
313	device_printf(bus, "%s called deactivate_resource\n",
314	    device_get_nameunit(child));
315	return (ENXIO);
316}
317
318static int
319siba_pcib_release_resource(device_t bus, device_t child, int type, int rid,
320    struct resource *r)
321{
322
323	device_printf(bus, "%s: not yet implemented\n", __func__);
324	device_printf(bus, "%s called release_resource\n",
325	    device_get_nameunit(child));
326	return (ENXIO);
327}
328
329/* pcib interface functions */
330
331static int
332siba_pcib_maxslots(device_t dev)
333{
334
335	return (SBPCI_SLOTMAX);
336}
337
338/*
339 * This needs hacking and fixery. It is currently broke and hangs.
340 * Debugging it will be tricky; there seems to be no way to enable
341 * a target abort which would cause a nice target abort.
342 * Look at linux again?
343 */
344static u_int32_t
345siba_pcib_read_config(device_t dev, u_int bus, u_int slot, u_int func,
346    u_int reg, int bytes)
347{
348	struct siba_pcib_softc *sc = device_get_softc(dev);
349	bus_addr_t cfgaddr;
350	uint32_t cfgtag;
351	uint32_t val;
352
353	/* XXX anything higher than slot 2 currently seems to hang the bus.
354	 * not sure why this is; look at linux again
355	 */
356	if (bus != 0 || slot > 2) {
357		printf("%s: bad b/s/f %d/%d/%d\n", __func__, bus, slot, func);
358		return 0xffffffff;	// XXX
359	}
360
361	device_printf(dev, "requested %d bytes from b/s/f %d/%d/%d reg %d\n",
362	    bytes, bus, slot, func, reg);
363
364	/*
365	 * The configuration tag on the broadcom is weird.
366	 */
367	SBPCI_WRITE_4(sc, SIBA_PCICORE_SBTOPCI1, 2);	/* XXX again??? */
368	cfgtag = ((1 << slot) << 16) | (func << 8);
369	cfgaddr = SBPCI_CFGBASE | cfgtag | (reg & ~3);
370
371	/* cfg space i/o is always 32 bits on this bridge */
372	printf("reading 4 bytes from %08x\n", cfgaddr);
373	val = *(volatile uint32_t *)MIPS_PHYS_TO_KSEG1(cfgaddr); /* XXX MIPS */
374
375	val = bswap32(val);	/* XXX seems to be needed for now */
376
377	/* swizzle and return what was asked for */
378	val &= 0xffffffff >> ((4 - bytes) * 8);
379
380	return (val);
381}
382
383static void
384siba_pcib_write_config(device_t dev, u_int bus, u_int slot,
385    u_int func, u_int reg, u_int32_t val, int bytes)
386{
387
388	/* write to pci configuration space */
389	//device_printf(dev, "%s: not yet implemented\n", __func__);
390}
391
392static int
393siba_pcib_route_interrupt(device_t bridge, device_t device, int pin)
394{
395
396	//device_printf(bridge, "%s: not yet implemented\n", __func__);
397	return (-1);
398}
399
400static device_method_t siba_pcib_methods[] = {
401	/* Device interface */
402	DEVMETHOD(device_attach,	siba_pcib_attach),
403	DEVMETHOD(device_probe,		siba_pcib_probe),
404
405	/* Bus interface */
406	DEVMETHOD(bus_read_ivar,	siba_pcib_read_ivar),
407	DEVMETHOD(bus_write_ivar,	siba_pcib_write_ivar),
408	DEVMETHOD(bus_setup_intr,	siba_pcib_setup_intr),
409	DEVMETHOD(bus_teardown_intr,	siba_pcib_teardown_intr),
410	DEVMETHOD(bus_alloc_resource,	siba_pcib_alloc_resource),
411	DEVMETHOD(bus_activate_resource,	siba_pcib_activate_resource),
412	DEVMETHOD(bus_deactivate_resource,	siba_pcib_deactivate_resource),
413	DEVMETHOD(bus_release_resource,	siba_pcib_release_resource),
414
415	/* pcib interface */
416	DEVMETHOD(pcib_maxslots,	siba_pcib_maxslots),
417	DEVMETHOD(pcib_read_config,	siba_pcib_read_config),
418	DEVMETHOD(pcib_write_config,	siba_pcib_write_config),
419	DEVMETHOD(pcib_route_interrupt,	siba_pcib_route_interrupt),
420
421	DEVMETHOD_END
422};
423
424static driver_t siba_pcib_driver = {
425	"pcib",
426	siba_pcib_methods,
427	sizeof(struct siba_softc),
428};
429static devclass_t siba_pcib_devclass;
430
431DRIVER_MODULE(siba_pcib, siba, siba_pcib_driver, siba_pcib_devclass, 0, 0);
432