ar71xx_pci.c revision 294883
1171095Ssam/*-
2171095Ssam * Copyright (c) 2009, Oleksandr Tymoshenko <gonzo@FreeBSD.org>
3171095Ssam * All rights reserved.
4171095Ssam *
5171095Ssam * Redistribution and use in source and binary forms, with or without
6171095Ssam * modification, are permitted provided that the following conditions
7171095Ssam * are met:
8171095Ssam * 1. Redistributions of source code must retain the above copyright
9171095Ssam *    notice unmodified, this list of conditions, and the following
10171095Ssam *    disclaimer.
11171095Ssam * 2. Redistributions in binary form must reproduce the above copyright
12171095Ssam *    notice, this list of conditions and the following disclaimer in the
13171095Ssam *    documentation and/or other materials provided with the distribution.
14171095Ssam *
15171095Ssam * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16171095Ssam * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17171095Ssam * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18171095Ssam * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19171095Ssam * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20171095Ssam * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21171095Ssam * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22171095Ssam * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23171095Ssam * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24171095Ssam * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25171095Ssam * SUCH DAMAGE.
26171095Ssam */
27171095Ssam
28171095Ssam#include <sys/cdefs.h>
29171095Ssam__FBSDID("$FreeBSD: head/sys/mips/atheros/ar71xx_pci.c 294883 2016-01-27 02:23:54Z jhibbits $");
30171095Ssam
31171095Ssam#include "opt_ar71xx.h"
32171095Ssam
33171095Ssam#include <sys/param.h>
34173139Srwatson#include <sys/systm.h>
35173139Srwatson
36173139Srwatson#include <sys/bus.h>
37173139Srwatson#include <sys/interrupt.h>
38173139Srwatson#include <sys/malloc.h>
39173139Srwatson#include <sys/kernel.h>
40173139Srwatson#include <sys/module.h>
41171095Ssam#include <sys/rman.h>
42171095Ssam#include <sys/lock.h>
43171095Ssam#include <sys/mutex.h>
44171095Ssam
45171095Ssam#include <vm/vm.h>
46171095Ssam#include <vm/pmap.h>
47171095Ssam#include <vm/vm_extern.h>
48171095Ssam
49173139Srwatson#include <machine/bus.h>
50173139Srwatson#include <machine/cpu.h>
51173139Srwatson#include <machine/intr_machdep.h>
52173139Srwatson#include <machine/pmap.h>
53171095Ssam
54171095Ssam#include <dev/pci/pcivar.h>
55171095Ssam#include <dev/pci/pcireg.h>
56171095Ssam
57171095Ssam#include <dev/pci/pcib_private.h>
58171095Ssam#include "pcib_if.h"
59171095Ssam
60171095Ssam#include <mips/atheros/ar71xxreg.h>
61171095Ssam#include <mips/atheros/ar71xx_pci_bus_space.h>
62171095Ssam
63171095Ssam#include <mips/atheros/ar71xx_cpudef.h>
64171095Ssam
65171095Ssam#ifdef	AR71XX_ATH_EEPROM
66171095Ssam#include <mips/atheros/ar71xx_fixup.h>
67171095Ssam#endif	/* AR71XX_ATH_EEPROM */
68171095Ssam
69171095Ssam#undef	AR71XX_PCI_DEBUG
70171095Ssam#ifdef	AR71XX_PCI_DEBUG
71171095Ssam#define	dprintf printf
72171095Ssam#else
73171095Ssam#define	dprintf(x, arg...)
74171095Ssam#endif
75171095Ssam
76171095Ssamstruct mtx ar71xx_pci_mtx;
77171095SsamMTX_SYSINIT(ar71xx_pci_mtx, &ar71xx_pci_mtx, "ar71xx PCI space mutex",
78171095Ssam    MTX_SPIN);
79171095Ssam
80173139Srwatsonstruct ar71xx_pci_softc {
81173139Srwatson	device_t		sc_dev;
82173139Srwatson
83173139Srwatson	int			sc_busno;
84173139Srwatson	int			sc_baseslot;
85173139Srwatson	struct rman		sc_mem_rman;
86173139Srwatson	struct rman		sc_irq_rman;
87173139Srwatson
88171095Ssam	struct intr_event	*sc_eventstab[AR71XX_PCI_NIRQS];
89171095Ssam	mips_intrcnt_t		sc_intr_counter[AR71XX_PCI_NIRQS];
90171095Ssam	struct resource		*sc_irq;
91171095Ssam	void			*sc_ih;
92171095Ssam};
93171095Ssam
94173139Srwatsonstatic int ar71xx_pci_setup_intr(device_t, device_t, struct resource *, int,
95173139Srwatson		    driver_filter_t *, driver_intr_t *, void *, void **);
96171095Ssamstatic int ar71xx_pci_teardown_intr(device_t, device_t, struct resource *,
97171095Ssam		    void *);
98171095Ssamstatic int ar71xx_pci_intr(void *);
99171095Ssam
100171095Ssamstatic void
101171095Ssamar71xx_pci_mask_irq(void *source)
102171095Ssam{
103171095Ssam	uint32_t reg;
104173139Srwatson	unsigned int irq = (unsigned int)source;
105173139Srwatson
106173139Srwatson	/* XXX is the PCI lock required here? */
107173139Srwatson	reg = ATH_READ_REG(AR71XX_PCI_INTR_MASK);
108173139Srwatson	/* flush */
109173139Srwatson	reg = ATH_READ_REG(AR71XX_PCI_INTR_MASK);
110173139Srwatson	ATH_WRITE_REG(AR71XX_PCI_INTR_MASK, reg & ~(1 << irq));
111171095Ssam}
112171095Ssam
113171095Ssamstatic void
114171095Ssamar71xx_pci_unmask_irq(void *source)
115171095Ssam{
116171095Ssam	uint32_t reg;
117171095Ssam	unsigned int irq = (unsigned int)source;
118171095Ssam
119171095Ssam	/* XXX is the PCI lock required here? */
120171095Ssam	reg = ATH_READ_REG(AR71XX_PCI_INTR_MASK);
121173139Srwatson	ATH_WRITE_REG(AR71XX_PCI_INTR_MASK, reg | (1 << irq));
122173139Srwatson	/* flush */
123173139Srwatson	reg = ATH_READ_REG(AR71XX_PCI_INTR_MASK);
124173139Srwatson}
125171095Ssam
126171095Ssam/*
127171095Ssam * get bitmask for bytes of interest:
128171095Ssam *   0 - we want this byte, 1 - ignore it. e.g: we read 1 byte
129173139Srwatson *   from register 7. Bitmask would be: 0111
130173139Srwatson */
131173139Srwatsonstatic uint32_t
132173139Srwatsonar71xx_get_bytes_to_read(int reg, int bytes)
133173139Srwatson{
134173139Srwatson	uint32_t bytes_to_read = 0;
135171095Ssam
136173139Srwatson	if ((bytes % 4) == 0)
137173139Srwatson		bytes_to_read = 0;
138171095Ssam	else if ((bytes % 4) == 1)
139173139Srwatson		bytes_to_read = (~(1 << (reg % 4))) & 0xf;
140173139Srwatson	else if ((bytes % 4) == 2)
141173139Srwatson		bytes_to_read = (~(3 << (reg % 4))) & 0xf;
142173139Srwatson	else
143173139Srwatson		panic("%s: wrong combination", __func__);
144173139Srwatson
145171095Ssam	return (bytes_to_read);
146173139Srwatson}
147173139Srwatson
148171095Ssamstatic int
149171095Ssamar71xx_pci_check_bus_error(void)
150171095Ssam{
151171095Ssam	uint32_t error, addr, has_errors = 0;
152171095Ssam
153171095Ssam	mtx_assert(&ar71xx_pci_mtx, MA_OWNED);
154171095Ssam
155171095Ssam	error = ATH_READ_REG(AR71XX_PCI_ERROR) & 0x3;
156171095Ssam	dprintf("%s: PCI error = %02x\n", __func__, error);
157171095Ssam	if (error) {
158171095Ssam		addr = ATH_READ_REG(AR71XX_PCI_ERROR_ADDR);
159171095Ssam
160171095Ssam		/* Do not report it yet */
161171095Ssam#if 0
162171095Ssam		printf("PCI bus error %d at addr 0x%08x\n", error, addr);
163171095Ssam#endif
164171095Ssam		ATH_WRITE_REG(AR71XX_PCI_ERROR, error);
165171095Ssam		has_errors = 1;
166171095Ssam	}
167171095Ssam
168171095Ssam	error = ATH_READ_REG(AR71XX_PCI_AHB_ERROR) & 0x1;
169171095Ssam	dprintf("%s: AHB error = %02x\n", __func__, error);
170171095Ssam	if (error) {
171171095Ssam		addr = ATH_READ_REG(AR71XX_PCI_AHB_ERROR_ADDR);
172171095Ssam		/* Do not report it yet */
173171095Ssam#if 0
174171095Ssam		printf("AHB bus error %d at addr 0x%08x\n", error, addr);
175173139Srwatson#endif
176171095Ssam		ATH_WRITE_REG(AR71XX_PCI_AHB_ERROR, error);
177171095Ssam		has_errors = 1;
178173139Srwatson	}
179171095Ssam
180171095Ssam	return (has_errors);
181171095Ssam}
182173139Srwatson
183171095Ssamstatic uint32_t
184171095Ssamar71xx_pci_make_addr(int bus, int slot, int func, int reg)
185173139Srwatson{
186173139Srwatson	if (bus == 0) {
187171095Ssam		return ((1 << slot) | (func << 8) | (reg & ~3));
188171095Ssam	} else {
189171095Ssam		return ((bus << 16) | (slot << 11) | (func << 8)
190171095Ssam		    | (reg  & ~3) | 1);
191173139Srwatson	}
192171095Ssam}
193171095Ssam
194171095Ssamstatic int
195171095Ssamar71xx_pci_conf_setup(int bus, int slot, int func, int reg, int bytes,
196171095Ssam    uint32_t cmd)
197171095Ssam{
198173139Srwatson	uint32_t addr = ar71xx_pci_make_addr(bus, slot, func, (reg & ~3));
199171095Ssam
200171095Ssam	mtx_assert(&ar71xx_pci_mtx, MA_OWNED);
201171095Ssam
202171095Ssam	cmd |= (ar71xx_get_bytes_to_read(reg, bytes) << 4);
203173139Srwatson	ATH_WRITE_REG(AR71XX_PCI_CONF_ADDR, addr);
204171095Ssam	ATH_WRITE_REG(AR71XX_PCI_CONF_CMD, cmd);
205171095Ssam
206173139Srwatson	dprintf("%s: tag (%x, %x, %x) %d/%d addr=%08x, cmd=%08x\n", __func__,
207171095Ssam	    bus, slot, func, reg, bytes, addr, cmd);
208171095Ssam
209171095Ssam	return ar71xx_pci_check_bus_error();
210171095Ssam}
211171095Ssam
212173139Srwatsonstatic uint32_t
213173139Srwatsonar71xx_pci_read_config(device_t dev, u_int bus, u_int slot, u_int func,
214173139Srwatson    u_int reg, int bytes)
215171095Ssam{
216171095Ssam	uint32_t data;
217171095Ssam	uint32_t shift, mask;
218171095Ssam
219171095Ssam	/* register access is 32-bit aligned */
220171095Ssam	shift = (reg & 3) * 8;
221171095Ssam
222171095Ssam	/* Create a mask based on the width, post-shift */
223171095Ssam	if (bytes == 2)
224171095Ssam		mask = 0xffff;
225171095Ssam	else if (bytes == 1)
226171095Ssam		mask = 0xff;
227171095Ssam	else
228171095Ssam		mask = 0xffffffff;
229171095Ssam
230171095Ssam	dprintf("%s: tag (%x, %x, %x) reg %d(%d)\n", __func__, bus, slot,
231171095Ssam	    func, reg, bytes);
232171095Ssam
233171095Ssam	mtx_lock_spin(&ar71xx_pci_mtx);
234173139Srwatson	 if (ar71xx_pci_conf_setup(bus, slot, func, reg, bytes,
235173139Srwatson	     PCI_CONF_CMD_READ) == 0)
236171095Ssam		 data = ATH_READ_REG(AR71XX_PCI_CONF_READ_DATA);
237171095Ssam	 else
238171095Ssam		 data = -1;
239171095Ssam	mtx_unlock_spin(&ar71xx_pci_mtx);
240173139Srwatson
241171095Ssam	/* get request bytes from 32-bit word */
242171095Ssam	data = (data >> shift) & mask;
243171095Ssam
244173139Srwatson	dprintf("%s: read 0x%x\n", __func__, data);
245173139Srwatson
246171095Ssam	return (data);
247171095Ssam}
248171095Ssam
249171095Ssamstatic void
250171095Ssamar71xx_pci_local_write(device_t dev, uint32_t reg, uint32_t data, int bytes)
251171095Ssam{
252171095Ssam	uint32_t cmd;
253171095Ssam
254171095Ssam	dprintf("%s: local write reg %d(%d)\n", __func__, reg, bytes);
255173139Srwatson
256171095Ssam	data = data << (8*(reg % 4));
257171095Ssam	cmd = PCI_LCONF_CMD_WRITE | (reg & ~3);
258173139Srwatson	cmd |= (ar71xx_get_bytes_to_read(reg, bytes) << 20);
259173139Srwatson	mtx_lock_spin(&ar71xx_pci_mtx);
260173139Srwatson	ATH_WRITE_REG(AR71XX_PCI_LCONF_CMD, cmd);
261173139Srwatson	ATH_WRITE_REG(AR71XX_PCI_LCONF_WRITE_DATA, data);
262173139Srwatson	mtx_unlock_spin(&ar71xx_pci_mtx);
263173139Srwatson}
264173139Srwatson
265173139Srwatsonstatic void
266171095Ssamar71xx_pci_write_config(device_t dev, u_int bus, u_int slot, u_int func,
267173139Srwatson    u_int reg, uint32_t data, int bytes)
268171095Ssam{
269171095Ssam
270171095Ssam	dprintf("%s: tag (%x, %x, %x) reg %d(%d)\n", __func__, bus, slot,
271173139Srwatson	    func, reg, bytes);
272173139Srwatson
273173139Srwatson	data = data << (8*(reg % 4));
274171095Ssam	mtx_lock_spin(&ar71xx_pci_mtx);
275173139Srwatson	 if (ar71xx_pci_conf_setup(bus, slot, func, reg, bytes,
276173139Srwatson	     PCI_CONF_CMD_WRITE) == 0)
277171095Ssam		 ATH_WRITE_REG(AR71XX_PCI_CONF_WRITE_DATA, data);
278171095Ssam	mtx_unlock_spin(&ar71xx_pci_mtx);
279173139Srwatson}
280173139Srwatson
281173139Srwatson#ifdef	AR71XX_ATH_EEPROM
282173139Srwatson/*
283173139Srwatson * Some embedded boards (eg AP94) have the MAC attached via PCI but they
284173139Srwatson * don't have the MAC-attached EEPROM.  The register initialisation
285173139Srwatson * values and calibration data are stored in the on-board flash.
286171095Ssam * This routine initialises the NIC via the EEPROM register contents
287171095Ssam * before the probe/attach routines get a go at things.
288171095Ssam */
289173139Srwatsonstatic void
290173139Srwatsonar71xx_pci_fixup(device_t dev, u_int bus, u_int slot, u_int func,
291171095Ssam    long flash_addr, int len)
292171095Ssam{
293171095Ssam	uint16_t *cal_data = (uint16_t *) MIPS_PHYS_TO_KSEG1(flash_addr);
294171095Ssam	uint32_t reg, val, bar0;
295171095Ssam
296173139Srwatson	if (bootverbose)
297173139Srwatson		device_printf(dev, "%s: flash_addr=%lx, cal_data=%p\n",
298171095Ssam		    __func__, flash_addr, cal_data);
299171095Ssam
300171095Ssam	/* XXX check 0xa55a */
301173139Srwatson	/* Save bar(0) address - just to flush bar(0) (SoC WAR) ? */
302173139Srwatson	bar0 = ar71xx_pci_read_config(dev, bus, slot, func, PCIR_BAR(0), 4);
303173139Srwatson	ar71xx_pci_write_config(dev, bus, slot, func, PCIR_BAR(0),
304173139Srwatson	    AR71XX_PCI_MEM_BASE, 4);
305171095Ssam
306171095Ssam	val = ar71xx_pci_read_config(dev, bus, slot, func, PCIR_COMMAND, 2);
307171095Ssam	val |= (PCIM_CMD_BUSMASTEREN | PCIM_CMD_MEMEN);
308171095Ssam	ar71xx_pci_write_config(dev, bus, slot, func, PCIR_COMMAND, val, 2);
309173139Srwatson
310173139Srwatson	cal_data += 3;
311171095Ssam	while (*cal_data != 0xffff) {
312171095Ssam		reg = *cal_data++;
313171095Ssam		val = *cal_data++;
314171095Ssam		val |= (*cal_data++) << 16;
315171095Ssam		if (bootverbose)
316171095Ssam			printf("  reg: %x, val=%x\n", reg, val);
317171095Ssam
318171095Ssam		/* Write eeprom fixup data to device memory */
319171095Ssam		ATH_WRITE_REG(AR71XX_PCI_MEM_BASE + reg, val);
320171095Ssam		DELAY(100);
321171095Ssam	}
322171095Ssam
323171095Ssam	val = ar71xx_pci_read_config(dev, bus, slot, func, PCIR_COMMAND, 2);
324173139Srwatson	val &= ~(PCIM_CMD_BUSMASTEREN | PCIM_CMD_MEMEN);
325171095Ssam	ar71xx_pci_write_config(dev, bus, slot, func, PCIR_COMMAND, val, 2);
326171095Ssam
327171095Ssam	/* Write the saved bar(0) address */
328171095Ssam	ar71xx_pci_write_config(dev, bus, slot, func, PCIR_BAR(0), bar0, 4);
329171095Ssam}
330171095Ssam
331171095Ssamstatic void
332171095Ssamar71xx_pci_slot_fixup(device_t dev, u_int bus, u_int slot, u_int func)
333171095Ssam{
334171095Ssam	long int flash_addr;
335171095Ssam	char buf[64];
336173139Srwatson	int size;
337171095Ssam
338173139Srwatson	/*
339173139Srwatson	 * Check whether the given slot has a hint to poke.
340171095Ssam	 */
341171095Ssam	if (bootverbose)
342171095Ssam	device_printf(dev, "%s: checking dev %s, %d/%d/%d\n",
343171095Ssam	    __func__, device_get_nameunit(dev), bus, slot, func);
344171095Ssam
345171095Ssam	snprintf(buf, sizeof(buf), "bus.%d.%d.%d.ath_fixup_addr",
346171095Ssam	    bus, slot, func);
347171095Ssam
348171095Ssam	if (resource_long_value(device_get_name(dev), device_get_unit(dev),
349171095Ssam	    buf, &flash_addr) == 0) {
350171095Ssam		snprintf(buf, sizeof(buf), "bus.%d.%d.%d.ath_fixup_size",
351171095Ssam		    bus, slot, func);
352171095Ssam		if (resource_int_value(device_get_name(dev),
353171095Ssam		    device_get_unit(dev), buf, &size) != 0) {
354171095Ssam			device_printf(dev,
355171095Ssam			    "%s: missing hint '%s', aborting EEPROM\n",
356171095Ssam			    __func__, buf);
357171095Ssam			return;
358171095Ssam		}
359171095Ssam
360171095Ssam
361171095Ssam		device_printf(dev, "found EEPROM at 0x%lx on %d.%d.%d\n",
362171095Ssam		    flash_addr, bus, slot, func);
363171095Ssam		ar71xx_pci_fixup(dev, bus, slot, func, flash_addr, size);
364171095Ssam		ar71xx_pci_slot_create_eeprom_firmware(dev, bus, slot, func,
365171095Ssam		    flash_addr, size);
366171095Ssam	}
367171095Ssam}
368171095Ssam#endif	/* AR71XX_ATH_EEPROM */
369171095Ssam
370171095Ssamstatic int
371171095Ssamar71xx_pci_probe(device_t dev)
372171095Ssam{
373171095Ssam
374171095Ssam	return (BUS_PROBE_NOWILDCARD);
375171095Ssam}
376171095Ssam
377171095Ssamstatic int
378173139Srwatsonar71xx_pci_attach(device_t dev)
379173139Srwatson{
380173139Srwatson	int rid = 0;
381173139Srwatson	struct ar71xx_pci_softc *sc = device_get_softc(dev);
382173139Srwatson
383171095Ssam	sc->sc_mem_rman.rm_type = RMAN_ARRAY;
384173139Srwatson	sc->sc_mem_rman.rm_descr = "ar71xx PCI memory window";
385173139Srwatson	if (rman_init(&sc->sc_mem_rman) != 0 ||
386173139Srwatson	    rman_manage_region(&sc->sc_mem_rman, AR71XX_PCI_MEM_BASE,
387173139Srwatson		AR71XX_PCI_MEM_BASE + AR71XX_PCI_MEM_SIZE - 1) != 0) {
388173139Srwatson		panic("ar71xx_pci_attach: failed to set up I/O rman");
389171095Ssam	}
390171095Ssam
391173139Srwatson	sc->sc_irq_rman.rm_type = RMAN_ARRAY;
392173139Srwatson	sc->sc_irq_rman.rm_descr = "ar71xx PCI IRQs";
393173139Srwatson	if (rman_init(&sc->sc_irq_rman) != 0 ||
394173139Srwatson	    rman_manage_region(&sc->sc_irq_rman, AR71XX_PCI_IRQ_START,
395171095Ssam	        AR71XX_PCI_IRQ_END) != 0)
396171095Ssam		panic("ar71xx_pci_attach: failed to set up IRQ rman");
397171095Ssam
398171095Ssam	/*
399171095Ssam	 * Check if there is a base slot hint. Otherwise use default value.
400173139Srwatson	 */
401173139Srwatson	if (resource_int_value(device_get_name(dev),
402171095Ssam	    device_get_unit(dev), "baseslot", &sc->sc_baseslot) != 0) {
403173139Srwatson		device_printf(dev,
404173139Srwatson		    "%s: missing hint '%s', default to AR71XX_PCI_BASE_SLOT\n",
405173139Srwatson		    __func__, "baseslot");
406173139Srwatson		sc->sc_baseslot = AR71XX_PCI_BASE_SLOT;
407173139Srwatson	}
408173139Srwatson
409173139Srwatson	ATH_WRITE_REG(AR71XX_PCI_INTR_STATUS, 0);
410171095Ssam	ATH_WRITE_REG(AR71XX_PCI_INTR_MASK, 0);
411173139Srwatson
412173139Srwatson	/* Hook up our interrupt handler. */
413173139Srwatson	if ((sc->sc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
414173139Srwatson	    RF_SHAREABLE | RF_ACTIVE)) == NULL) {
415173139Srwatson		device_printf(dev, "unable to allocate IRQ resource\n");
416173139Srwatson		return ENXIO;
417173139Srwatson	}
418173139Srwatson
419173139Srwatson	if ((bus_setup_intr(dev, sc->sc_irq, INTR_TYPE_MISC,
420173139Srwatson			    ar71xx_pci_intr, NULL, sc, &sc->sc_ih))) {
421173139Srwatson		device_printf(dev,
422173139Srwatson		    "WARNING: unable to register interrupt handler\n");
423173139Srwatson		return ENXIO;
424173139Srwatson	}
425173139Srwatson
426173139Srwatson	/* reset PCI core and PCI bus */
427173139Srwatson	ar71xx_device_stop(RST_RESET_PCI_CORE | RST_RESET_PCI_BUS);
428173139Srwatson	DELAY(100000);
429173139Srwatson
430173139Srwatson	ar71xx_device_start(RST_RESET_PCI_CORE | RST_RESET_PCI_BUS);
431171095Ssam	DELAY(100000);
432171095Ssam
433171095Ssam	/* Init PCI windows */
434171095Ssam	ATH_WRITE_REG(AR71XX_PCI_WINDOW0, PCI_WINDOW0_ADDR);
435171095Ssam	ATH_WRITE_REG(AR71XX_PCI_WINDOW1, PCI_WINDOW1_ADDR);
436171095Ssam	ATH_WRITE_REG(AR71XX_PCI_WINDOW2, PCI_WINDOW2_ADDR);
437171095Ssam	ATH_WRITE_REG(AR71XX_PCI_WINDOW3, PCI_WINDOW3_ADDR);
438171095Ssam	ATH_WRITE_REG(AR71XX_PCI_WINDOW4, PCI_WINDOW4_ADDR);
439171095Ssam	ATH_WRITE_REG(AR71XX_PCI_WINDOW5, PCI_WINDOW5_ADDR);
440173139Srwatson	ATH_WRITE_REG(AR71XX_PCI_WINDOW6, PCI_WINDOW6_ADDR);
441173139Srwatson	ATH_WRITE_REG(AR71XX_PCI_WINDOW7, PCI_WINDOW7_CONF_ADDR);
442173139Srwatson	DELAY(100000);
443173139Srwatson
444171095Ssam	mtx_lock_spin(&ar71xx_pci_mtx);
445171095Ssam	ar71xx_pci_check_bus_error();
446171095Ssam	mtx_unlock_spin(&ar71xx_pci_mtx);
447171095Ssam
448171095Ssam	/* Fixup internal PCI bridge */
449171095Ssam	ar71xx_pci_local_write(dev, PCIR_COMMAND,
450171095Ssam            PCIM_CMD_BUSMASTEREN | PCIM_CMD_MEMEN
451171095Ssam	    | PCIM_CMD_SERRESPEN | PCIM_CMD_BACKTOBACK
452173139Srwatson	    | PCIM_CMD_PERRESPEN | PCIM_CMD_MWRICEN, 4);
453173139Srwatson
454173139Srwatson#ifdef	AR71XX_ATH_EEPROM
455171095Ssam	/*
456171095Ssam	 * Hard-code a check for slot 17 and 18 - these are
457171095Ssam	 * the two PCI slots which may have a PCI device that
458171095Ssam	 * requires "fixing".
459171095Ssam	 */
460171095Ssam	ar71xx_pci_slot_fixup(dev, 0, 17, 0);
461171095Ssam	ar71xx_pci_slot_fixup(dev, 0, 18, 0);
462173139Srwatson#endif	/* AR71XX_ATH_EEPROM */
463173139Srwatson
464171095Ssam	device_add_child(dev, "pci", -1);
465173139Srwatson	return (bus_generic_attach(dev));
466173139Srwatson}
467173139Srwatson
468173139Srwatsonstatic int
469171095Ssamar71xx_pci_read_ivar(device_t dev, device_t child, int which,
470173139Srwatson    uintptr_t *result)
471173139Srwatson{
472173139Srwatson	struct ar71xx_pci_softc *sc = device_get_softc(dev);
473173139Srwatson
474173139Srwatson	switch (which) {
475173139Srwatson	case PCIB_IVAR_DOMAIN:
476173139Srwatson		*result = 0;
477173139Srwatson		return (0);
478173139Srwatson	case PCIB_IVAR_BUS:
479173139Srwatson		*result = sc->sc_busno;
480173139Srwatson		return (0);
481171095Ssam	}
482173139Srwatson
483173139Srwatson	return (ENOENT);
484173139Srwatson}
485173139Srwatson
486171095Ssamstatic int
487171095Ssamar71xx_pci_write_ivar(device_t dev, device_t child, int which,
488171095Ssam    uintptr_t result)
489171095Ssam{
490171095Ssam	struct ar71xx_pci_softc * sc = device_get_softc(dev);
491171095Ssam
492171095Ssam	switch (which) {
493171095Ssam	case PCIB_IVAR_BUS:
494171095Ssam		sc->sc_busno = result;
495173139Srwatson		return (0);
496171095Ssam	}
497171095Ssam
498171095Ssam	return (ENOENT);
499171095Ssam}
500171095Ssam
501171095Ssamstatic struct resource *
502173139Srwatsonar71xx_pci_alloc_resource(device_t bus, device_t child, int type, int *rid,
503173139Srwatson    rman_res_t start, rman_res_t end, rman_res_t count, u_int flags)
504173139Srwatson{
505173139Srwatson
506173139Srwatson	struct ar71xx_pci_softc *sc = device_get_softc(bus);
507171095Ssam	struct resource *rv;
508173139Srwatson	struct rman *rm;
509171095Ssam
510171095Ssam	switch (type) {
511173139Srwatson	case SYS_RES_IRQ:
512171095Ssam		rm = &sc->sc_irq_rman;
513171095Ssam		break;
514171095Ssam	case SYS_RES_MEMORY:
515171095Ssam		rm = &sc->sc_mem_rman;
516171095Ssam		break;
517171095Ssam	default:
518171095Ssam		return (NULL);
519171095Ssam	}
520173139Srwatson
521171095Ssam	rv = rman_reserve_resource(rm, start, end, count, flags, child);
522171095Ssam
523171095Ssam	if (rv == NULL)
524171095Ssam		return (NULL);
525171095Ssam
526173139Srwatson	rman_set_rid(rv, *rid);
527171095Ssam
528171095Ssam	if (flags & RF_ACTIVE) {
529171095Ssam		if (bus_activate_resource(child, type, *rid, rv)) {
530171095Ssam			rman_release_resource(rv);
531171095Ssam			return (NULL);
532171095Ssam		}
533171095Ssam	}
534171095Ssam	return (rv);
535173139Srwatson}
536173139Srwatson
537173139Srwatsonstatic int
538173139Srwatsonar71xx_pci_activate_resource(device_t bus, device_t child, int type, int rid,
539173139Srwatson    struct resource *r)
540173139Srwatson{
541171095Ssam	int res = (BUS_ACTIVATE_RESOURCE(device_get_parent(bus),
542173139Srwatson	    child, type, rid, r));
543171095Ssam
544171095Ssam	if (!res) {
545171095Ssam		switch(type) {
546171095Ssam		case SYS_RES_MEMORY:
547173139Srwatson		case SYS_RES_IOPORT:
548173139Srwatson			rman_set_bustag(r, ar71xx_bus_space_pcimem);
549173139Srwatson			break;
550171095Ssam		}
551171095Ssam	}
552173139Srwatson	return (res);
553173139Srwatson}
554171095Ssam
555171095Ssamstatic int
556171095Ssamar71xx_pci_setup_intr(device_t bus, device_t child, struct resource *ires,
557171095Ssam	    int flags, driver_filter_t *filt, driver_intr_t *handler,
558171095Ssam	    void *arg, void **cookiep)
559171095Ssam{
560171095Ssam	struct ar71xx_pci_softc *sc = device_get_softc(bus);
561	struct intr_event *event;
562	int irq, error;
563
564	irq = rman_get_start(ires);
565
566	if (irq > AR71XX_PCI_IRQ_END)
567		panic("%s: bad irq %d", __func__, irq);
568
569	event = sc->sc_eventstab[irq];
570	if (event == NULL) {
571		error = intr_event_create(&event, (void *)irq, 0, irq,
572		    ar71xx_pci_mask_irq, ar71xx_pci_unmask_irq, NULL, NULL,
573		    "pci intr%d:", irq);
574
575		if (error == 0) {
576			sc->sc_eventstab[irq] = event;
577			sc->sc_intr_counter[irq] =
578			    mips_intrcnt_create(event->ie_name);
579		}
580		else
581			return (error);
582	}
583
584	intr_event_add_handler(event, device_get_nameunit(child), filt,
585	    handler, arg, intr_priority(flags), flags, cookiep);
586	mips_intrcnt_setname(sc->sc_intr_counter[irq], event->ie_fullname);
587
588	ar71xx_pci_unmask_irq((void*)irq);
589
590	return (0);
591}
592
593static int
594ar71xx_pci_teardown_intr(device_t dev, device_t child, struct resource *ires,
595    void *cookie)
596{
597	struct ar71xx_pci_softc *sc = device_get_softc(dev);
598	int irq, result;
599
600	irq = rman_get_start(ires);
601	if (irq > AR71XX_PCI_IRQ_END)
602		panic("%s: bad irq %d", __func__, irq);
603
604	if (sc->sc_eventstab[irq] == NULL)
605		panic("Trying to teardown unoccupied IRQ");
606
607	ar71xx_pci_mask_irq((void*)irq);
608
609	result = intr_event_remove_handler(cookie);
610	if (!result)
611		sc->sc_eventstab[irq] = NULL;
612
613	return (result);
614}
615
616static int
617ar71xx_pci_intr(void *arg)
618{
619	struct ar71xx_pci_softc *sc = arg;
620	struct intr_event *event;
621	uint32_t reg, irq, mask;
622
623	reg = ATH_READ_REG(AR71XX_PCI_INTR_STATUS);
624	mask = ATH_READ_REG(AR71XX_PCI_INTR_MASK);
625	/*
626	 * Handle only unmasked interrupts
627	 */
628	reg &= mask;
629	for (irq = AR71XX_PCI_IRQ_START; irq <= AR71XX_PCI_IRQ_END; irq++) {
630		if (reg & (1 << irq)) {
631			event = sc->sc_eventstab[irq];
632			if (!event || TAILQ_EMPTY(&event->ie_handlers)) {
633				/* Ignore timer interrupts */
634				if (irq != 0)
635					printf("Stray IRQ %d\n", irq);
636				continue;
637			}
638
639			/* Flush DDR FIFO for PCI/PCIe */
640			ar71xx_device_flush_ddr(AR71XX_CPU_DDR_FLUSH_PCIE);
641
642			/* TODO: frame instead of NULL? */
643			intr_event_handle(event, NULL);
644			mips_intrcnt_inc(sc->sc_intr_counter[irq]);
645		}
646	}
647
648	return (FILTER_HANDLED);
649}
650
651static int
652ar71xx_pci_maxslots(device_t dev)
653{
654
655	return (PCI_SLOTMAX);
656}
657
658static int
659ar71xx_pci_route_interrupt(device_t pcib, device_t device, int pin)
660{
661	struct ar71xx_pci_softc *sc = device_get_softc(pcib);
662
663	if (pci_get_slot(device) < sc->sc_baseslot)
664		panic("%s: PCI slot %d is less then AR71XX_PCI_BASE_SLOT",
665		    __func__, pci_get_slot(device));
666
667	return (pci_get_slot(device) - sc->sc_baseslot);
668}
669
670static device_method_t ar71xx_pci_methods[] = {
671	/* Device interface */
672	DEVMETHOD(device_probe,		ar71xx_pci_probe),
673	DEVMETHOD(device_attach,	ar71xx_pci_attach),
674	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
675	DEVMETHOD(device_suspend,	bus_generic_suspend),
676	DEVMETHOD(device_resume,	bus_generic_resume),
677
678	/* Bus interface */
679	DEVMETHOD(bus_read_ivar,	ar71xx_pci_read_ivar),
680	DEVMETHOD(bus_write_ivar,	ar71xx_pci_write_ivar),
681	DEVMETHOD(bus_alloc_resource,	ar71xx_pci_alloc_resource),
682	DEVMETHOD(bus_release_resource,	bus_generic_release_resource),
683	DEVMETHOD(bus_activate_resource, ar71xx_pci_activate_resource),
684	DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
685	DEVMETHOD(bus_setup_intr,	ar71xx_pci_setup_intr),
686	DEVMETHOD(bus_teardown_intr,	ar71xx_pci_teardown_intr),
687
688	/* pcib interface */
689	DEVMETHOD(pcib_maxslots,	ar71xx_pci_maxslots),
690	DEVMETHOD(pcib_read_config,	ar71xx_pci_read_config),
691	DEVMETHOD(pcib_write_config,	ar71xx_pci_write_config),
692	DEVMETHOD(pcib_route_interrupt,	ar71xx_pci_route_interrupt),
693
694	DEVMETHOD_END
695};
696
697static driver_t ar71xx_pci_driver = {
698	"pcib",
699	ar71xx_pci_methods,
700	sizeof(struct ar71xx_pci_softc),
701};
702
703static devclass_t ar71xx_pci_devclass;
704
705DRIVER_MODULE(ar71xx_pci, nexus, ar71xx_pci_driver, ar71xx_pci_devclass, 0, 0);
706