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