ht.c revision 1.9
1/*	$OpenBSD: ht.c,v 1.9 2006/03/13 20:10:49 brad 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	pba.pba_bridgetag = NULL;
194
195	printf(": %d devices\n", sc->sc_maxdevs);
196
197	extern void fix_node_irq(int, struct pcibus_attach_args *);
198
199	for (node = OF_child(ca->ca_node); node; node = nn) {
200		fix_node_irq(node, &pba);
201
202		if ((nn = OF_child(node)) != 0)
203			continue;
204
205		while ((nn = OF_peer(node)) == 0) {
206			node = OF_parent(node);
207			if (node == ca->ca_node) {
208				nn = 0;
209				break;
210			}
211		}
212	}
213
214	config_found(self, &pba, ht_print);
215}
216
217void
218ht_attach_hook(struct device *parent, struct device *self,
219    struct pcibus_attach_args *pba)
220{
221}
222
223int
224ht_bus_maxdevs(void *cpv, int bus)
225{
226	struct ht_softc *sc = cpv;
227
228	/* XXX Probing more busses doesn't work. */
229	if (bus == 0)
230		return sc->sc_maxdevs;
231	return 32;
232}
233
234pcitag_t
235ht_make_tag(void *cpv, int bus, int dev, int fnc)
236{
237	return (bus << BUS_SHIFT) | (dev << DEVICE_SHIFT) | (fnc << FNC_SHIFT);
238}
239
240void
241ht_decompose_tag(void *cpv, pcitag_t tag, int *busp, int *devp, int *fncp)
242{
243	if (busp != NULL)
244		*busp = (tag >> BUS_SHIFT) & 0xff;
245	if (devp != NULL)
246		*devp = (tag >> DEVICE_SHIFT) & 0x1f;
247	if (fncp != NULL)
248		*fncp = (tag >> FNC_SHIFT) & 0x7;
249}
250
251pcireg_t
252ht_conf_read(void *cpv, pcitag_t tag, int offset)
253{
254	struct ht_softc *sc = cpv;
255	int bus, dev, fcn;
256	pcireg_t reg;
257
258#ifdef DEBUG
259	printf("ht_conf_read: tag=%x, offset=%x\n", tag, offset);
260#endif
261	ht_decompose_tag(NULL, tag, &bus, &dev, &fcn);
262	if (bus == 0 && dev == 0) {
263		tag |= (offset << 2);
264		reg = bus_space_read_4(sc->sc_iot, sc->sc_config0_ioh, tag);
265		reg = letoh32(reg);
266	} else if (bus == 0) {
267		/* XXX Why can we only access function 0? */
268		if (fcn > 0)
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, fcn;
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, &fcn);
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		/* XXX Why can we only access function 0? */
300		if (fcn > 0)
301			return;
302		tag |= offset;
303		bus_space_write_4(sc->sc_memt, sc->sc_config0_memh, tag, data);
304		bus_space_read_4(sc->sc_memt, sc->sc_config0_memh, tag);
305	} else {
306		tag |= offset;
307		bus_space_write_4(sc->sc_memt, sc->sc_config1_memh, tag, data);
308		bus_space_read_4(sc->sc_memt, sc->sc_config1_memh, tag);
309	}
310}
311
312/* XXX */
313#define PCI_INTERRUPT_NO_CONNECTION	0xff
314
315int
316ht_intr_map(void *cpv, pcitag_t tag, int pin, int line,
317    pci_intr_handle_t *ihp)
318{
319	int error = 0;
320
321#ifdef DEBUG
322	printf("ht_intr_map: tag=%x, pin=%d, line=%d\n", tag, pin, line);
323#endif
324
325	*ihp = -1;
326        if (line == PCI_INTERRUPT_NO_CONNECTION)
327                error = 1; /* No IRQ used. */
328        else if (pin > PCI_INTERRUPT_PIN_MAX) {
329                printf("ht_intr_map: bad interrupt pin %d\n", pin);
330                error = 1;
331        }
332
333	if (!error)
334		*ihp = line;
335	return error;
336}
337
338const char *
339ht_intr_string(void *cpv, pci_intr_handle_t ih)
340{
341	static char str[16];
342
343	snprintf(str, sizeof str, "irq %ld", ih);
344	return (str);
345}
346
347int
348ht_intr_line(void *cpv, pci_intr_handle_t ih)
349{
350	return (ih);
351}
352
353void *
354ht_intr_establish(void *cpv, pci_intr_handle_t ih, int level,
355    int (*func)(void *), void *arg, char *name)
356{
357	return (*intr_establish_func)(cpv, ih, IST_LEVEL, level, func, arg,
358		name);
359}
360
361void
362ht_intr_disestablish(void *lcv, void *cookie)
363{
364}
365
366int
367ht_ether_hw_addr(struct ppc_pci_chipset *lcpc, u_int8_t *oaddr)
368{
369	u_int8_t laddr[6];
370	int node;
371	int len;
372
373	node = OF_finddevice("enet");
374	len = OF_getprop(node, "local-mac-address", laddr, sizeof(laddr));
375	if (sizeof(laddr) == len) {
376		memcpy(oaddr, laddr, sizeof(laddr));
377		return 1;
378	}
379
380	oaddr[0] = oaddr[1] = oaddr[2] = 0xff;
381	oaddr[3] = oaddr[4] = oaddr[5] = 0xff;
382	return 0;
383}
384
385int
386ht_print(void *aux, const char *pnp)
387{
388	struct pcibus_attach_args *pba = aux;
389
390	if (pnp)
391		printf("%s at %s", pba->pba_busname, pnp);
392	printf(" bus %d", pba->pba_bus);
393	return (UNCONF);
394}
395