ht.c revision 1.5
1/*	$OpenBSD: ht.c,v 1.5 2005/09/30 21:37:21 kettenis Exp $	*/
2
3/*
4 * Copyright (c) 2005 Mark Kettenis
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19#include <sys/param.h>
20#include <sys/systm.h>
21#include <sys/device.h>
22
23#include <machine/autoconf.h>
24#include <machine/bus.h>
25
26#include <dev/pci/pcireg.h>
27#include <dev/pci/pcivar.h>
28#include <dev/pci/pcidevs.h>
29
30#include <macppc/pci/pcibrvar.h>
31
32#include <dev/ofw/openfirm.h>
33
34int	 ht_match(struct device *, void *, void *);
35void	 ht_attach(struct device *, struct device *, void *);
36
37void	 ht_attach_hook(struct device *, struct device *,
38	     struct pcibus_attach_args *);
39int	 ht_bus_maxdevs(void *, int);
40pcitag_t ht_make_tag(void *, int, int, int);
41void	 ht_decompose_tag(void *, pcitag_t, int *, int *, int *);
42pcireg_t ht_conf_read(void *, pcitag_t, int);
43void	 ht_conf_write(void *, pcitag_t, int, pcireg_t);
44int	 ht_intr_map(void *, pcitag_t, int, int, pci_intr_handle_t *);
45const char *ht_intr_string(void *, pci_intr_handle_t);
46int	 ht_intr_line(void *, pci_intr_handle_t);
47void	*ht_intr_establish(void *, pci_intr_handle_t, int, int (*)(void *),
48	     void *, char *);
49void	 ht_intr_disestablish(void *, void *);
50
51int	 ht_ether_hw_addr(struct ppc_pci_chipset *, u_int8_t *);
52
53int	 ht_print(void *, const char *);
54
55struct ht_softc {
56	struct device	sc_dev;
57	int		sc_maxdevs;
58	struct ppc_bus_space sc_mem_bus_space;
59	struct ppc_bus_space sc_io_bus_space;
60	struct ppc_pci_chipset sc_pc;
61	bus_space_tag_t sc_memt;
62	bus_space_handle_t sc_config0_memh;
63	bus_space_handle_t sc_config1_memh;
64	bus_space_tag_t sc_iot;
65	bus_space_handle_t sc_config0_ioh;
66};
67
68struct cfattach ht_ca = {
69	sizeof(struct ht_softc), ht_match, ht_attach
70};
71
72struct cfdriver ht_cd = {
73	NULL, "ht", DV_DULL,
74};
75
76#if 0
77struct powerpc_bus_dma_tag pci_bus_dma_tag = {
78	NULL,
79	_dmamap_create,
80	_dmamap_destroy,
81	_dmamap_load,
82	_dmamap_load_mbuf,
83	_dmamap_load_uio,
84	_dmamap_load_raw,
85	_dmamap_unload,
86	_dmamap_sync,
87	_dmamem_alloc,
88	_dmamem_free,
89	_dmamem_map,
90	_dmamem_unmap,
91	_dmamem_mmap
92};
93#else
94extern struct powerpc_bus_dma_tag pci_bus_dma_tag;
95#endif
96
97int
98ht_match(struct device *parent, void *cf, void *aux)
99{
100	struct confargs *ca = aux;
101
102	if (strcmp(ca->ca_name, "ht") == 0)
103		return (1);
104	return (0);
105}
106
107void
108ht_attach(struct device *parent, struct device *self, void *aux)
109{
110	struct ht_softc *sc = (struct ht_softc *)self;
111	struct confargs *ca = aux;
112	struct pcibus_attach_args pba;
113	u_int32_t regs[6];
114	char compat[32];
115	int node, nn;
116	int len;
117
118	if (ca->ca_node == 0) {
119		printf("invalid node on ht config\n");
120		return;
121	}
122
123	len = OF_getprop(ca->ca_node, "reg", regs, sizeof(regs));
124	if (len < sizeof(regs)) {
125		printf(": regs lookup failed, node %x\n", ca->ca_node);
126		return;
127	}
128
129	sc->sc_mem_bus_space.bus_base = 0x80000000;
130	sc->sc_mem_bus_space.bus_size = 0;
131	sc->sc_mem_bus_space.bus_io = 0;
132	sc->sc_memt = &sc->sc_mem_bus_space;
133
134	sc->sc_io_bus_space.bus_base = 0x80000000;
135	sc->sc_io_bus_space.bus_size = 0;
136	sc->sc_io_bus_space.bus_io = 1;
137	sc->sc_iot = &sc->sc_io_bus_space;
138
139	if (bus_space_map(sc->sc_memt, regs[1], 0x4000, 0,
140		&sc->sc_config0_memh)) {
141		printf(": can't map PCI config0 memory\n");
142		return;
143	}
144
145	if (bus_space_map(sc->sc_memt, regs[1] + 0x01000000, 0x80000, 0,
146		&sc->sc_config1_memh)) {
147		printf(": can't map PCI config1 memory\n");
148		return;
149	}
150
151	if (bus_space_map(sc->sc_iot, regs[4], 0x1000, 0,
152		&sc->sc_config0_ioh)) {
153		printf(": can't map PCI config0 io\n");
154		return;
155	}
156
157	len = OF_getprop(ca->ca_node, "compatible", compat, sizeof(compat));
158	if (len <= 0)
159		printf(": unknown");
160	else
161		printf(": %s", compat);
162
163	sc->sc_pc.pc_conf_v = sc;
164	sc->sc_pc.pc_attach_hook = ht_attach_hook;
165	sc->sc_pc.pc_bus_maxdevs = ht_bus_maxdevs;
166	sc->sc_pc.pc_make_tag = ht_make_tag;
167	sc->sc_pc.pc_decompose_tag = ht_decompose_tag;
168	sc->sc_pc.pc_conf_read = ht_conf_read;
169	sc->sc_pc.pc_conf_write = ht_conf_write;
170
171	sc->sc_pc.pc_intr_v = sc;
172	sc->sc_pc.pc_intr_map = ht_intr_map;
173	sc->sc_pc.pc_intr_string = ht_intr_string;
174	sc->sc_pc.pc_intr_line = ht_intr_line;
175	sc->sc_pc.pc_intr_establish = ht_intr_establish;
176	sc->sc_pc.pc_intr_disestablish = ht_intr_disestablish;
177	sc->sc_pc.pc_ether_hw_addr = ht_ether_hw_addr;
178
179	pba.pba_busname = "pci";
180	pba.pba_iot = sc->sc_iot;
181	pba.pba_memt = sc->sc_memt;
182	pba.pba_dmat = &pci_bus_dma_tag;
183	pba.pba_pc = &sc->sc_pc;
184	pba.pba_bus = 0;
185
186	sc->sc_maxdevs = 1;
187	for (node = OF_child(ca->ca_node); node; node = OF_peer(node))
188		sc->sc_maxdevs++;
189	printf(": %d devices\n", sc->sc_maxdevs);
190
191	extern void fix_node_irq(int, struct pcibus_attach_args *);
192
193	for (node = OF_child(ca->ca_node); node; node = nn) {
194		fix_node_irq(node, &pba);
195
196		if ((nn = OF_child(node)) != 0)
197			continue;
198
199		while ((nn = OF_peer(node)) == 0) {
200			node = OF_parent(node);
201			if (node == ca->ca_node) {
202				nn = 0;
203				break;
204			}
205		}
206	}
207
208	config_found(self, &pba, ht_print);
209}
210
211void
212ht_attach_hook(struct device *parent, struct device *self,
213    struct pcibus_attach_args *pba)
214{
215}
216
217int
218ht_bus_maxdevs(void *cpv, int bus)
219{
220	struct ht_softc *sc = cpv;
221
222	/* XXX Probing more busses doesn't work. */
223	if (bus == 0)
224		return sc->sc_maxdevs;
225	return 32;
226}
227
228#define BUS_SHIFT 16
229#define DEVICE_SHIFT 11
230#define FNC_SHIFT 8
231
232pcitag_t
233ht_make_tag(void *cpv, int bus, int dev, int fnc)
234{
235	return (bus << BUS_SHIFT) | (dev << DEVICE_SHIFT) | (fnc << FNC_SHIFT);
236}
237
238void
239ht_decompose_tag(void *cpv, pcitag_t tag, int *busp, int *devp, int *fncp)
240{
241	if (busp != NULL)
242		*busp = (tag >> BUS_SHIFT) & 0xff;
243	if (devp != NULL)
244		*devp = (tag >> DEVICE_SHIFT) & 0x1f;
245	if (fncp != NULL)
246		*fncp = (tag >> FNC_SHIFT) & 0x7;
247}
248
249pcireg_t
250ht_conf_read(void *cpv, pcitag_t tag, int offset)
251{
252	struct ht_softc *sc = cpv;
253	int bus, dev, fcn;
254	pcireg_t reg;
255
256#ifdef DEBUG
257	printf("ht_conf_read: tag=%x, offset=%x\n", tag, offset);
258#endif
259	ht_decompose_tag(NULL, tag, &bus, &dev, &fcn);
260	if (bus == 0 && dev == 0) {
261		tag |= (offset << 2);
262		reg = bus_space_read_4(sc->sc_iot, sc->sc_config0_ioh, tag);
263		reg = letoh32(reg);
264	} else if (bus == 0) {
265		if (tag >= 0x4000)
266			panic("tag >= 0x4000");
267		/* XXX Needed on some PowerMac G5's.  Why? */
268		if (fcn > 1)
269			return ~0;
270		tag |= offset;
271		reg = bus_space_read_4(sc->sc_memt, sc->sc_config0_memh, tag);
272	} else {
273		tag |= offset;
274		reg = bus_space_read_4(sc->sc_memt, sc->sc_config1_memh, tag);
275	}
276#ifdef DEBUG
277	printf("ht_conf_read: reg=%x\n", reg);
278#endif
279	return reg;
280}
281
282void
283ht_conf_write(void *cpv, pcitag_t tag, int offset, pcireg_t data)
284{
285	struct ht_softc *sc = cpv;
286	int bus, dev;
287
288#ifdef DEBUG
289	printf("ht_conf_write: tag=%x, offset=%x, data = %x\n",
290	       tag, offset, data);
291#endif
292	ht_decompose_tag(NULL, tag, &bus, &dev, NULL);
293	if (bus == 0 && dev == 0) {
294		tag |= (offset << 2);
295		data = htole32(data);
296		bus_space_write_4(sc->sc_iot, sc->sc_config0_ioh, tag, data);
297		bus_space_read_4(sc->sc_iot, sc->sc_config0_ioh, tag);
298	} else if (bus == 0) {
299		tag |= offset;
300		bus_space_write_4(sc->sc_memt, sc->sc_config0_memh, tag, data);
301		bus_space_read_4(sc->sc_memt, sc->sc_config0_memh, tag);
302	} else {
303		tag |= offset;
304		bus_space_write_4(sc->sc_memt, sc->sc_config1_memh, tag, data);
305		bus_space_read_4(sc->sc_memt, sc->sc_config1_memh, tag);
306	}
307}
308
309/* XXX */
310#define PCI_INTERRUPT_NO_CONNECTION	0xff
311
312int
313ht_intr_map(void *cpv, pcitag_t tag, int pin, int line,
314    pci_intr_handle_t *ihp)
315{
316	int error = 0;
317
318#ifdef DEBUG
319	printf("ht_intr_map: tag=%x, pin=%d, line=%d\n", tag, pin, line);
320#endif
321
322	*ihp = -1;
323        if (pin == PCI_INTERRUPT_PIN_NONE ||
324	    line == PCI_INTERRUPT_NO_CONNECTION)
325                error = 1; /* No IRQ used. */
326        else if (pin > PCI_INTERRUPT_PIN_MAX) {
327                printf("ht_intr_map: bad interrupt pin %d\n", pin);
328                error = 1;
329        }
330
331	if (!error)
332		*ihp = line;
333	return error;
334}
335
336const char *
337ht_intr_string(void *cpv, pci_intr_handle_t ih)
338{
339	static char str[16];
340
341	snprintf(str, sizeof str, "irq %ld", ih);
342	return (str);
343}
344
345int
346ht_intr_line(void *cpv, pci_intr_handle_t ih)
347{
348	return (ih);
349}
350
351void *
352ht_intr_establish(void *cpv, pci_intr_handle_t ih, int level,
353    int (*func)(void *), void *arg, char *name)
354{
355	return (*intr_establish_func)(cpv, ih, IST_LEVEL, level, func, arg,
356		name);
357}
358
359void
360ht_intr_disestablish(void *lcv, void *cookie)
361{
362}
363
364int
365ht_ether_hw_addr(struct ppc_pci_chipset *lcpc, u_int8_t *oaddr)
366{
367	u_int8_t laddr[6];
368	int node;
369	int len;
370
371	node = OF_finddevice("enet");
372	len = OF_getprop(node, "local-mac-address", laddr, sizeof(laddr));
373	if (sizeof(laddr) == len) {
374		memcpy(oaddr, laddr, sizeof(laddr));
375		return 1;
376	}
377
378	oaddr[0] = oaddr[1] = oaddr[2] = 0xff;
379	oaddr[3] = oaddr[4] = oaddr[5] = 0xff;
380	return 0;
381}
382
383int
384ht_print(void *aux, const char *pnp)
385{
386	struct pcibus_attach_args *pba = aux;
387
388	if (pnp)
389		printf("%s at %s", pba->pba_busname, pnp);
390	printf(" bus %d", pba->pba_bus);
391	return (UNCONF);
392}
393