1/*	$NetBSD: uninorth.c,v 1.15 2011/06/30 00:52:58 matt Exp $	*/
2
3/*-
4 * Copyright (c) 2000 Tsubai Masanari.  All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 * 3. The name of the author may not be used to endorse or promote products
15 *    derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include <sys/cdefs.h>
30__KERNEL_RCSID(0, "$NetBSD: uninorth.c,v 1.15 2011/06/30 00:52:58 matt Exp $");
31
32#include <sys/param.h>
33#include <sys/device.h>
34#include <sys/systm.h>
35
36#include <dev/pci/pcivar.h>
37#include <dev/ofw/openfirm.h>
38#include <dev/ofw/ofw_pci.h>
39
40#include <machine/autoconf.h>
41#include <machine/pio.h>
42
43struct uninorth_softc {
44	device_t sc_dev;
45	struct genppc_pci_chipset sc_pc;
46	struct powerpc_bus_space sc_iot;
47	struct powerpc_bus_space sc_memt;
48};
49
50static void uninorth_attach(device_t, device_t, void *);
51static int uninorth_match(device_t, cfdata_t, void *);
52
53static pcireg_t uninorth_conf_read(void *, pcitag_t, int);
54static void uninorth_conf_write(void *, pcitag_t, int, pcireg_t);
55
56CFATTACH_DECL_NEW(uninorth, sizeof(struct uninorth_softc),
57    uninorth_match, uninorth_attach, NULL, NULL);
58
59static int
60uninorth_match(device_t parent, cfdata_t cf, void *aux)
61{
62	struct confargs *ca = aux;
63	char compat[32];
64
65	if (strcmp(ca->ca_name, "pci") != 0)
66		return 0;
67
68	memset(compat, 0, sizeof(compat));
69	OF_getprop(ca->ca_node, "compatible", compat, sizeof(compat));
70	if (strcmp(compat, "uni-north") != 0)
71		return 0;
72
73	return 1;
74}
75
76static void
77uninorth_attach(device_t parent, device_t self, void *aux)
78{
79	struct uninorth_softc *sc = device_private(self);
80	pci_chipset_tag_t pc = &sc->sc_pc;
81	struct confargs *ca = aux;
82	struct pcibus_attach_args pba;
83	int len, child, node = ca->ca_node;
84	uint32_t reg[2], busrange[2];
85	struct ranges {
86		uint32_t pci_hi, pci_mid, pci_lo;
87		uint32_t host;
88		uint32_t size_hi, size_lo;
89	} ranges[6], *rp = ranges;
90
91	printf("\n");
92	sc->sc_dev = self;
93
94	/* UniNorth address */
95	if (OF_getprop(node, "reg", reg, sizeof(reg)) < 8)
96		return;
97
98	/* PCI bus number */
99	if (OF_getprop(node, "bus-range", busrange, sizeof(busrange)) != 8)
100		return;
101
102	/* find i/o tag */
103	len = OF_getprop(node, "ranges", ranges, sizeof(ranges));
104	if (len == -1)
105		return;
106	while (len >= sizeof(ranges[0])) {
107		if ((rp->pci_hi & OFW_PCI_PHYS_HI_SPACEMASK) ==
108		     OFW_PCI_PHYS_HI_SPACE_IO) {
109			sc->sc_iot.pbs_base = rp->host;
110			sc->sc_iot.pbs_limit = rp->host + rp->size_lo;
111			break;
112		}
113		len -= sizeof(ranges[0]);
114		rp++;
115	}
116
117	/* XXX enable gmac ethernet */
118	for (child = OF_child(node); child; child = OF_peer(child)) {
119		volatile int *gmac_gbclock_en = (void *)0xf8000020;
120		char compat[32];
121
122		memset(compat, 0, sizeof(compat));
123		OF_getprop(child, "compatible", compat, sizeof(compat));
124		if (strcmp(compat, "gmac") == 0)
125			*gmac_gbclock_en |= 0x02;
126	}
127
128	sc->sc_iot.pbs_flags = _BUS_SPACE_LITTLE_ENDIAN|_BUS_SPACE_IO_TYPE;
129	sc->sc_iot.pbs_offset = 0;
130	if (ofwoea_map_space(RANGE_TYPE_PCI, RANGE_IO, node, &sc->sc_iot,
131	    "uninorth io-space") != 0)
132		panic("Can't init uninorth io tag");
133
134	sc->sc_memt.pbs_flags = _BUS_SPACE_LITTLE_ENDIAN|_BUS_SPACE_MEM_TYPE;
135	sc->sc_memt.pbs_base = 0x00000000;
136	if (ofwoea_map_space(RANGE_TYPE_PCI, RANGE_MEM, node, &sc->sc_memt,
137	    "uninorth mem-space") != 0)
138		panic("Can't init uninorth mem tag");
139
140	macppc_pci_get_chipset_tag(pc);
141	pc->pc_node = node;
142	pc->pc_addr = mapiodev(reg[0] + 0x800000, 4, false);
143	pc->pc_data = mapiodev(reg[0] + 0xc00000, 8, false);
144	pc->pc_bus = busrange[0];
145	pc->pc_conf_read = uninorth_conf_read;
146	pc->pc_conf_write = uninorth_conf_write;
147	pc->pc_iot = &sc->sc_iot;
148	pc->pc_memt = &sc->sc_memt;
149
150	memset(&pba, 0, sizeof(pba));
151	pba.pba_memt = pc->pc_memt;
152	pba.pba_iot = pc->pc_iot;
153	pba.pba_dmat = &pci_bus_dma_tag;
154	pba.pba_dmat64 = NULL;
155	pba.pba_bus = pc->pc_bus;
156	pba.pba_bridgetag = NULL;
157	pba.pba_pc = pc;
158	pba.pba_flags = PCI_FLAGS_IO_OKAY | PCI_FLAGS_MEM_OKAY;
159
160	config_found_ia(self, "pcibus", &pba, pcibusprint);
161}
162
163static pcireg_t
164uninorth_conf_read(void *cookie, pcitag_t tag, int reg)
165{
166	pci_chipset_tag_t pc = cookie;
167	int32_t *daddr = pc->pc_data;
168	pcireg_t data;
169	int bus, dev, func, s;
170	uint32_t x;
171
172	/* UniNorth seems to have a 64bit data port */
173	if (reg & 0x04)
174		daddr++;
175
176	pci_decompose_tag(pc, tag, &bus, &dev, &func);
177
178	/*
179	 * bandit's minimum device number of the first bus is 11.
180	 * So we behave as if there is no device when dev < 11.
181	 */
182	if (func > 7)
183		panic("pci_conf_read: func > 7");
184
185	if (bus == pc->pc_bus) {
186		if (dev < 11)
187			return 0xffffffff;
188		x = (1 << dev) | (func << 8) | reg;
189	} else
190		x = tag | reg | 1;
191
192	s = splhigh();
193
194	out32rb(pc->pc_addr, x);
195	in32rb(pc->pc_addr);
196	data = 0xffffffff;
197	if (!badaddr(daddr, 4))
198		data = in32rb(daddr);
199	out32rb(pc->pc_addr, 0);
200	in32rb(pc->pc_addr);
201	splx(s);
202
203	return data;
204}
205
206static void
207uninorth_conf_write(void *cookie, pcitag_t tag, int reg, pcireg_t data)
208{
209	pci_chipset_tag_t pc = cookie;
210	int32_t *daddr = pc->pc_data;
211	int bus, dev, func, s;
212	uint32_t x;
213
214	/* UniNorth seems to have a 64bit data port */
215	if (reg & 0x04)
216		daddr++;
217
218	pci_decompose_tag(pc, tag, &bus, &dev, &func);
219
220	if (func > 7)
221		panic("pci_conf_write: func > 7");
222
223	if (bus == pc->pc_bus) {
224		if (dev < 11)
225			panic("pci_conf_write: dev < 11");
226		x = (1 << dev) | (func << 8) | reg;
227	} else
228		x = tag | reg | 1;
229
230	s = splhigh();
231
232	out32rb(pc->pc_addr, x);
233	in32rb(pc->pc_addr);
234	out32rb(daddr, data);
235	out32rb(pc->pc_addr, 0);
236	in32rb(pc->pc_addr);
237
238	splx(s);
239}
240