159191Skris/*	$NetBSD: pci_gio.c,v 1.20 2023/12/20 15:29:07 thorpej Exp $	*/
259191Skris
359191Skris/*
459191Skris * Copyright (c) 2006 Stephen M. Rumble
5109998Smarkm * All rights reserved.
6109998Smarkm *
7109998Smarkm * Redistribution and use in source and binary forms, with or without
8109998Smarkm * modification, are permitted provided that the following conditions
9109998Smarkm * are met:
10109998Smarkm * 1. Redistributions of source code must retain the above copyright
11109998Smarkm *    notice, this list of conditions and the following disclaimer.
12109998Smarkm * 2. The name of the author may not be used to endorse or promote products
13109998Smarkm *    derived from this software without specific prior written permission.
14109998Smarkm *
15109998Smarkm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16109998Smarkm * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17109998Smarkm * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18109998Smarkm * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19109998Smarkm * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2059191Skris * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2159191Skris * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2259191Skris * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2359191Skris * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2459191Skris * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25160814Ssimon */
26109998Smarkm
27109998Smarkm#include <sys/cdefs.h>
28109998Smarkm__KERNEL_RCSID(0, "$NetBSD: pci_gio.c,v 1.20 2023/12/20 15:29:07 thorpej Exp $");
29109998Smarkm
30109998Smarkm/*
31109998Smarkm * Glue for PCI devices that are connected to the GIO bus by various little
32109998Smarkm * GIO<->PCI ASICs.
33109998Smarkm *
34109998Smarkm * We presently support the following boards:
35109998Smarkm *	o Phobos G100/G130/G160	(if_tlp, lxtphy)
36109998Smarkm *	o Set Engineering GFE	(if_tl, nsphy)
37109998Smarkm */
38109998Smarkm
39109998Smarkm#include "opt_pci.h"
40109998Smarkm#include "pci.h"
41109998Smarkm
42109998Smarkm#include <sys/param.h>
43109998Smarkm#include <sys/systm.h>
44109998Smarkm#include <sys/device.h>
45109998Smarkm
46109998Smarkm#include <sys/bus.h>
47109998Smarkm#include <machine/machtype.h>
4868651Skris
4959191Skris#include <sgimips/gio/giovar.h>
5068651Skris#include <sgimips/gio/gioreg.h>
5159191Skris#include <sgimips/gio/giodevs.h>
5259191Skris
5368651Skris#include <sgimips/dev/imcvar.h>
5459191Skris
5559191Skris#include <mips/cache.h>
5659191Skris
5759191Skris#include <dev/pci/pcivar.h>
5868651Skris#include <dev/pci/pcireg.h>
5959191Skris#include <dev/pci/pcidevs.h>
6059191Skris#include <dev/pci/pciconf.h>
6159191Skris
6259191Skrisint giopci_debug = 0;
63109998Smarkm#define DPRINTF(_x)	if (giopci_debug) printf _x
6468651Skris
6568651Skrisstruct giopci_softc {
6668651Skris	struct sgimips_pci_chipset	sc_pc;
6759191Skris	int				sc_slot;
6859191Skris	int				sc_gprid;
6959191Skris	uint32_t			sc_pci_len;
7059191Skris	bus_space_tag_t			sc_iot;
7159191Skris	bus_space_handle_t		sc_ioh;
7259191Skris};
7359191Skris
7459191Skrisstatic int	giopci_match(device_t, cfdata_t, void *);
7568651Skrisstatic void	giopci_attach(device_t, device_t, void *);
7668651Skrisstatic int	giopci_bus_maxdevs(pci_chipset_tag_t, int);
7768651Skrisstatic pcireg_t	giopci_conf_read(pci_chipset_tag_t, pcitag_t, int);
7868651Skrisstatic void	giopci_conf_write(pci_chipset_tag_t, pcitag_t, int, pcireg_t);
7959191Skrisstatic int	giopci_conf_hook(pci_chipset_tag_t, int, int, int, pcireg_t);
8059191Skrisstatic int	giopci_intr_map(const struct pci_attach_args *,
8159191Skris		    pci_intr_handle_t *);
8259191Skrisstatic const char *
8368651Skris		giopci_intr_string(pci_chipset_tag_t, pci_intr_handle_t,
8459191Skris		    char *, size_t);
8568651Skrisstatic void    *giopci_intr_establish(int, int, int (*)(void *), void *);
8668651Skrisstatic void	giopci_intr_disestablish(void *);
8759191Skris
8868651Skris#define PHOBOS_PCI_OFFSET	0x00100000
8968651Skris#define PHOBOS_PCI_LENGTH	128		/* ~arbitrary */
9059191Skris#define PHOBOS_TULIP_START	0x00101000
9159191Skris#define PHOBOS_TULIP_END	0x001fffff
9259191Skris
9359191Skris#define SETENG_MAGIC_OFFSET	0x00020000
9459191Skris#define SETENG_MAGIC_VALUE	0x00001000
9559191Skris#define SETENG_PCI_OFFSET	0x00080000
9659191Skris#define SETENG_PCI_LENGTH	128		/* ~arbitrary */
9759191Skris#define SETENG_TLAN_START	0x00100000
9859191Skris#define SETENG_TLAN_END		0x001fffff
99109998Smarkm
10059191SkrisCFATTACH_DECL_NEW(giopci, sizeof(struct giopci_softc),
101109998Smarkm    giopci_match, giopci_attach, NULL, NULL);
102109998Smarkm
103109998Smarkmstatic void pcimem_bus_mem_init(bus_space_tag_t, void *);
104109998Smarkmstatic struct mips_bus_space	pcimem_mbst;
105109998Smarkmbus_space_tag_t	gio_pci_memt = NULL;
106109998Smarkm
107109998Smarkmstatic int
108109998Smarkmgiopci_match(device_t parent, cfdata_t match, void *aux)
109109998Smarkm{
110109998Smarkm	struct gio_attach_args *ga = aux;
111109998Smarkm	int gprid;
112109998Smarkm
11359191Skris	/*
11459191Skris	 * I think that these cards are all GIO32-bis or GIO64. Thus
11559191Skris	 * they work in either Indigo2/Challenge M or
11659191Skris	 * Indy/Challenge S/Indigo R4k, according to form factor. However,
11759191Skris	 * there are some exceptions (e.g. my Indigo R4k won't power
11859191Skris	 * on with the Set Engineering card installed).
119109998Smarkm	 */
12059191Skris	if (mach_type != MACH_SGI_IP20 && mach_type != MACH_SGI_IP22)
121109998Smarkm		return (0);
122109998Smarkm
123109998Smarkm	gprid = GIO_PRODUCT_PRODUCTID(ga->ga_product);
124109998Smarkm	if (gprid == PHOBOS_G100 || gprid == PHOBOS_G130 ||
125109998Smarkm	    gprid == PHOBOS_G160 || gprid == SETENG_GFE)
126109998Smarkm		return (1);
127109998Smarkm
12859191Skris	return (0);
129109998Smarkm}
130109998Smarkm
131109998Smarkmstatic void
132109998Smarkmgiopci_attach(device_t parent, device_t self, void *aux)
133109998Smarkm{
13459191Skris	struct giopci_softc *sc = device_private(self);
135109998Smarkm	pci_chipset_tag_t pc = &sc->sc_pc;
136109998Smarkm	struct gio_attach_args *ga = aux;
137109998Smarkm	uint32_t pci_off, pci_len, arb;
138109998Smarkm	struct pcibus_attach_args pba;
139109998Smarkm	u_long m_start, m_end;
140109998Smarkm#ifdef PCI_NETBSD_CONFIGURE
14159191Skris	extern int pci_conf_debug;
142109998Smarkm
143109998Smarkm	pci_conf_debug = giopci_debug;
144109998Smarkm#endif
145109998Smarkm
146109998Smarkm	sc->sc_iot	= ga->ga_iot;
14759191Skris	sc->sc_slot	= ga->ga_slot;
148109998Smarkm	sc->sc_gprid	= GIO_PRODUCT_PRODUCTID(ga->ga_product);
149109998Smarkm
150109998Smarkm	pcimem_bus_mem_init(&pcimem_mbst, NULL);
151109998Smarkm	gio_pci_memt = &pcimem_mbst;
15259191Skris
153109998Smarkm	if (mach_type == MACH_SGI_IP22 &&
154109998Smarkm	    mach_subtype == MACH_SGI_IP22_FULLHOUSE)
155109998Smarkm		arb = GIO_ARB_RT | GIO_ARB_MST | GIO_ARB_PIPE;
156109998Smarkm	else
157109998Smarkm		arb = GIO_ARB_RT | GIO_ARB_MST;
158109998Smarkm
159109998Smarkm	if (gio_arb_config(ga->ga_slot, arb)) {
160109998Smarkm		printf(": failed to configure GIO bus arbiter\n");
161109998Smarkm		return;
162109998Smarkm	}
16359191Skris
16459191Skris#if (NIMC > 0)
16559191Skris	imc_disable_sysad_parity();
16659191Skris#endif
16759191Skris
16859191Skris	switch (sc->sc_gprid) {
16959191Skris	case PHOBOS_G100:
17059191Skris	case PHOBOS_G130:
17159191Skris	case PHOBOS_G160:
172109998Smarkm		pci_off = PHOBOS_PCI_OFFSET;
173109998Smarkm		pci_len = PHOBOS_PCI_LENGTH;
174109998Smarkm		m_start = MIPS_KSEG1_TO_PHYS(ga->ga_addr + PHOBOS_TULIP_START);
175109998Smarkm		m_end = MIPS_KSEG1_TO_PHYS(ga->ga_addr + PHOBOS_TULIP_END);
176109998Smarkm		break;
177109998Smarkm
178109998Smarkm	case SETENG_GFE:
17959191Skris		/*
18059191Skris		 * NB: The SetEng board does not allow the ThunderLAN's DMA
18159191Skris		 *     engine to properly transfer segments that span page
18268651Skris		 *     boundaries. See sgimips/autoconf.c where we catch a
18368651Skris		 *     tl(4) device attachment and create an appropriate
18468651Skris		 *     proplib entry to enable the workaround.
18559191Skris		 */
18668651Skris		pci_off = SETENG_PCI_OFFSET;
18768651Skris		pci_len = SETENG_PCI_LENGTH;
18868651Skris		m_start = MIPS_KSEG1_TO_PHYS(ga->ga_addr + SETENG_TLAN_START);
18968651Skris		m_end = MIPS_KSEG1_TO_PHYS(ga->ga_addr + SETENG_TLAN_END);
19059191Skris		bus_space_write_4(ga->ga_iot, ga->ga_ioh,
19159191Skris		    SETENG_MAGIC_OFFSET, SETENG_MAGIC_VALUE);
19259191Skris		break;
19359191Skris
19459191Skris	default:
19559191Skris		panic("giopci_attach: unsupported GIO product id 0x%02x",
19659191Skris		    sc->sc_gprid);
19759191Skris	}
19859191Skris
19959191Skris	if (bus_space_subregion(ga->ga_iot, ga->ga_ioh, pci_off, pci_len,
20059191Skris	    &sc->sc_ioh)) {
20159191Skris		printf("%s: unable to map PCI registers\n", device_xname(self));
20259191Skris		return;
20359191Skris	}
20459191Skris	sc->sc_pci_len = pci_len;
20559191Skris
20659191Skris	pc->pc_bus_maxdevs	= giopci_bus_maxdevs;
20759191Skris	pc->pc_conf_read	= giopci_conf_read;
20859191Skris	pc->pc_conf_write	= giopci_conf_write;
20959191Skris	pc->pc_conf_hook	= giopci_conf_hook;
21068651Skris	pc->pc_intr_map		= giopci_intr_map;
21168651Skris	pc->pc_intr_string	= giopci_intr_string;
21268651Skris	pc->intr_establish	= giopci_intr_establish;
21368651Skris	pc->intr_disestablish	= giopci_intr_disestablish;
21468651Skris	pc->iot			= ga->ga_iot;
21559191Skris	pc->ioh			= ga->ga_ioh;
21659191Skris	pc->cookie		= sc;
21759191Skris
21859191Skris	printf(": %s\n", gio_product_string(sc->sc_gprid));
21959191Skris
22059191Skris#ifdef PCI_NETBSD_CONFIGURE
22159191Skris	struct pciconf_resources *pcires = pciconf_resource_init();
22259191Skris
22359191Skris	pciconf_resource_add(pcires, PCICONF_RESOURCE_MEM,
22459191Skris	    m_start, (m_end - m_start) + 1);
22559191Skris
22659191Skris	pci_configure_bus(pc, pcires, 0,
22759191Skris	    mips_cache_info.mci_dcache_align);
22859191Skris
22959191Skris	pciconf_resource_fini(pcires);
23059191Skris#endif
23168651Skris
23259191Skris	memset(&pba, 0, sizeof(pba));
23368651Skris	pba.pba_memt	= gio_pci_memt;
23468651Skris	pba.pba_dmat	= ga->ga_dmat;
23568651Skris	pba.pba_pc	= pc;
23668651Skris	pba.pba_flags	= PCI_FLAGS_MEM_OKAY;
23759191Skris	/* NB: do not set PCI_FLAGS_{MRL,MRM,MWI}_OKAY  -- true ?! */
23859191Skris
239160814Ssimon	config_found(self, &pba, pcibusprint, CFARGS_NONE);
240160814Ssimon}
24159191Skris
242109998Smarkmstatic int
243109998Smarkmgiopci_bus_maxdevs(pci_chipset_tag_t pc, int busno)
24459191Skris{
245109998Smarkm
246109998Smarkm	return (busno == 0);
24759191Skris}
24868651Skris
24959191Skrisstatic pcireg_t
25059191Skrisgiopci_conf_read(pci_chipset_tag_t pc, pcitag_t tag, int reg)
25159191Skris{
25259191Skris	struct giopci_softc *sc = pc->cookie;
25359191Skris	int bus, dev, func;
25459191Skris	pcireg_t data;
25559191Skris
25659191Skris	if ((unsigned int)reg >= PCI_CONF_SIZE)
25759191Skris		return (pcireg_t) -1;
25859191Skris
25959191Skris	pci_decompose_tag(pc, tag, &bus, &dev, &func);
26059191Skris	if (bus != 0 || dev != 0 || func != 0)
261109998Smarkm		return (0);
262109998Smarkm
26359191Skris	/* XXX - should just use bus_space_peek */
26459191Skris	if (reg >= sc->sc_pci_len) {
26559191Skris		DPRINTF(("giopci_conf_read: reg 0x%x out of bounds\n", reg));
26659191Skris		return (0);
26759191Skris	}
26859191Skris
26959191Skris	DPRINTF(("giopci_conf_read: reg 0x%x = 0x", reg));
27059191Skris	data = bus_space_read_4(sc->sc_iot, sc->sc_ioh, reg);
27159191Skris	DPRINTF(("%08x\n", data));
27259191Skris
27359191Skris	return (data);
27468651Skris}
27568651Skris
27668651Skrisstatic void
27768651Skrisgiopci_conf_write(pci_chipset_tag_t pc, pcitag_t tag, int reg, pcireg_t data)
27868651Skris{
27968651Skris	struct giopci_softc *sc = pc->cookie;
28068651Skris	int bus, dev, func;
28168651Skris
28268651Skris	if ((unsigned int)reg >= PCI_CONF_SIZE)
28368651Skris		return;
28468651Skris
28568651Skris	pci_decompose_tag(pc, tag, &bus, &dev, &func);
28668651Skris	if (bus != 0 || dev != 0 || func != 0)
28768651Skris		return;
28868651Skris
28968651Skris	/* XXX - should just use bus_space_poke */
29068651Skris	if (reg >= sc->sc_pci_len) {
29168651Skris		DPRINTF(("giopci_conf_write: reg 0x%x out of bounds "
29268651Skris		    "(val = 0x%08x)\n", reg, data));
29368651Skris		return;
29468651Skris	}
29568651Skris
29668651Skris	DPRINTF(("giopci_conf_write: reg 0x%x = 0x%08x\n", reg, data));
29768651Skris	bus_space_write_4(sc->sc_iot, sc->sc_ioh, reg, data);
29868651Skris}
29968651Skris
30068651Skrisstatic int
30168651Skrisgiopci_conf_hook(pci_chipset_tag_t pc, int bus, int device, int function,
30268651Skris    pcireg_t id)
30368651Skris{
30468651Skris
30568651Skris	/* All devices use memory accesses only. */
30668651Skris	return (PCI_CONF_MAP_MEM | PCI_CONF_ENABLE_MEM | PCI_CONF_ENABLE_BM);
30768651Skris}
30868651Skris
30968651Skrisstatic int
31068651Skrisgiopci_intr_map(const struct pci_attach_args *pa, pci_intr_handle_t *ihp)
31168651Skris{
31268651Skris	struct giopci_softc *sc = pa->pa_pc->cookie;
31368651Skris
31468651Skris	*ihp = sc->sc_slot;
31568651Skris
31668651Skris	return (0);
31768651Skris}
31868651Skris
31968651Skrisstatic const char *
32068651Skrisgiopci_intr_string(pci_chipset_tag_t pc, pci_intr_handle_t ih, char * buf,
32168651Skris    size_t len)
32268651Skris{
32368651Skris	snprintf(buf, len, "slot %s", (ih == GIO_SLOT_EXP0) ? "EXP0" :
32468651Skris	    (ih == GIO_SLOT_EXP1) ? "EXP1" : "GFX");
32568651Skris	return buf;
32668651Skris}
32768651Skris
32868651Skrisstatic void *
32968651Skrisgiopci_intr_establish(int slot, int level, int (*func)(void *), void *arg)
33068651Skris{
33168651Skris
33268651Skris	return (gio_intr_establish(slot, level, func, arg));
33368651Skris}
33468651Skris
33568651Skrisstatic void
33668651Skrisgiopci_intr_disestablish(void *cookie)
33768651Skris{
33868651Skris
33968651Skris	panic("giopci_intr_disestablish: impossible.");
34068651Skris}
34168651Skris
34268651Skris#define CHIP	   		pcimem
34359191Skris#define	CHIP_MEM		/* defined */
34459191Skris#define CHIP_WRONG_ENDIAN
34559191Skris
34659191Skris#define	CHIP_W1_BUS_START(v)	0x00000000UL
34759191Skris#define CHIP_W1_BUS_END(v)	0xffffffffUL
34859191Skris#define	CHIP_W1_SYS_START(v)	0x00000000UL
34959191Skris#define	CHIP_W1_SYS_END(v)	0xffffffffUL
35059191Skris
35159191Skris#include <mips/mips/bus_space_alignstride_chipdep.c>
35259191Skris