]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - sys/powerpc/powermac/cpcht.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.git] / sys / powerpc / powermac / cpcht.c
1 /*-
2  * Copyright (C) 2008-2010 Nathan Whitehorn
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17  * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
18  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
20  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
21  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
22  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
23  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  *
25  * $FreeBSD$
26  */
27
28 #include <sys/param.h>
29 #include <sys/systm.h>
30 #include <sys/module.h>
31 #include <sys/bus.h>
32 #include <sys/conf.h>
33 #include <sys/kernel.h>
34
35 #include <dev/ofw/openfirm.h>
36 #include <dev/ofw/ofw_pci.h>
37
38 #include <dev/pci/pcivar.h>
39 #include <dev/pci/pcireg.h>
40
41 #include <machine/bus.h>
42 #include <machine/intr_machdep.h>
43 #include <machine/md_var.h>
44 #include <machine/openpicvar.h>
45 #include <machine/pio.h>
46 #include <machine/resource.h>
47
48 #include <sys/rman.h>
49
50 #include <dev/ofw/ofw_bus.h>
51 #include <dev/ofw/ofw_bus_subr.h>
52
53 #include <vm/vm.h>
54 #include <vm/pmap.h>
55
56 #include "pcib_if.h"
57 #include "pic_if.h"
58
59 /*
60  * IBM CPC9X5 Hypertransport Device interface.
61  */
62 static int              cpcht_probe(device_t);
63 static int              cpcht_attach(device_t);
64
65 static void             cpcht_configure_htbridge(device_t, phandle_t);
66
67 /*
68  * Bus interface.
69  */
70 static int              cpcht_read_ivar(device_t, device_t, int,
71                             uintptr_t *);
72 static struct resource *cpcht_alloc_resource(device_t bus, device_t child,
73                             int type, int *rid, u_long start, u_long end,
74                             u_long count, u_int flags);
75 static int              cpcht_activate_resource(device_t bus, device_t child,
76                             int type, int rid, struct resource *res);
77 static int              cpcht_release_resource(device_t bus, device_t child,
78                             int type, int rid, struct resource *res);
79 static int              cpcht_deactivate_resource(device_t bus, device_t child,
80                             int type, int rid, struct resource *res);
81
82 /*
83  * pcib interface.
84  */
85 static int              cpcht_maxslots(device_t);
86 static u_int32_t        cpcht_read_config(device_t, u_int, u_int, u_int,
87                             u_int, int);
88 static void             cpcht_write_config(device_t, u_int, u_int, u_int,
89                             u_int, u_int32_t, int);
90 static int              cpcht_route_interrupt(device_t bus, device_t dev,
91                             int pin);
92
93 /*
94  * ofw_bus interface
95  */
96
97 static phandle_t        cpcht_get_node(device_t bus, device_t child);
98
99 /*
100  * Driver methods.
101  */
102 static device_method_t  cpcht_methods[] = {
103         /* Device interface */
104         DEVMETHOD(device_probe,         cpcht_probe),
105         DEVMETHOD(device_attach,        cpcht_attach),
106
107         /* Bus interface */
108         DEVMETHOD(bus_print_child,      bus_generic_print_child),
109         DEVMETHOD(bus_read_ivar,        cpcht_read_ivar),
110         DEVMETHOD(bus_setup_intr,       bus_generic_setup_intr),
111         DEVMETHOD(bus_teardown_intr,    bus_generic_teardown_intr),
112         DEVMETHOD(bus_alloc_resource,   cpcht_alloc_resource),
113         DEVMETHOD(bus_release_resource, cpcht_release_resource),
114         DEVMETHOD(bus_activate_resource,        cpcht_activate_resource),
115         DEVMETHOD(bus_deactivate_resource,      cpcht_deactivate_resource),
116
117         /* pcib interface */
118         DEVMETHOD(pcib_maxslots,        cpcht_maxslots),
119         DEVMETHOD(pcib_read_config,     cpcht_read_config),
120         DEVMETHOD(pcib_write_config,    cpcht_write_config),
121         DEVMETHOD(pcib_route_interrupt, cpcht_route_interrupt),
122
123         /* ofw_bus interface */
124         DEVMETHOD(ofw_bus_get_node,     cpcht_get_node),
125         { 0, 0 }
126 };
127
128 struct cpcht_irq {
129         int             ht_source;
130
131         vm_offset_t     ht_base;
132         vm_offset_t     apple_eoi;
133         uint32_t        eoi_data;
134         int             edge;
135 };
136
137 static struct cpcht_irq *cpcht_irqmap = NULL;
138
139 struct cpcht_softc {
140         device_t                sc_dev;
141         phandle_t               sc_node;
142         vm_offset_t             sc_data;
143         uint64_t                sc_populated_slots;
144         struct                  rman sc_mem_rman;
145
146         struct cpcht_irq        htirq_map[128];
147 };
148
149 static driver_t cpcht_driver = {
150         "pcib",
151         cpcht_methods,
152         sizeof(struct cpcht_softc)
153 };
154
155 static devclass_t       cpcht_devclass;
156
157 DRIVER_MODULE(cpcht, nexus, cpcht_driver, cpcht_devclass, 0, 0);
158
159 #define HTAPIC_REQUEST_EOI      0x20
160 #define HTAPIC_TRIGGER_LEVEL    0x02
161 #define HTAPIC_MASK             0x01
162
163 struct cpcht_range {
164         u_int32_t       pci_hi;
165         u_int32_t       pci_mid;
166         u_int32_t       pci_lo;
167         u_int32_t       junk;
168         u_int32_t       host_hi;
169         u_int32_t       host_lo;
170         u_int32_t       size_hi;
171         u_int32_t       size_lo;
172 };
173
174 static int
175 cpcht_probe(device_t dev)
176 {
177         const char      *type, *compatible;
178
179         type = ofw_bus_get_type(dev);
180         compatible = ofw_bus_get_compat(dev);
181
182         if (type == NULL || compatible == NULL)
183                 return (ENXIO);
184
185         if (strcmp(type, "ht") != 0)
186                 return (ENXIO);
187
188         if (strcmp(compatible, "u3-ht") != 0)
189                 return (ENXIO);
190
191
192         device_set_desc(dev, "IBM CPC9X5 HyperTransport Tunnel");
193         return (0);
194 }
195
196 static int
197 cpcht_attach(device_t dev)
198 {
199         struct          cpcht_softc *sc;
200         phandle_t       node, child;
201         u_int32_t       reg[3];
202         int             error;
203
204         node = ofw_bus_get_node(dev);
205         sc = device_get_softc(dev);
206
207         if (OF_getprop(node, "reg", reg, sizeof(reg)) < 12)
208                 return (ENXIO);
209
210         sc->sc_dev = dev;
211         sc->sc_node = node;
212         sc->sc_populated_slots = 0;
213         sc->sc_data = (vm_offset_t)pmap_mapdev(reg[1], reg[2]);
214
215         sc->sc_mem_rman.rm_type = RMAN_ARRAY;
216         sc->sc_mem_rman.rm_descr = "CPCHT Device Memory";
217         error = rman_init(&sc->sc_mem_rman);
218
219         if (error) {
220                 device_printf(dev, "rman_init() failed. error = %d\n", error);
221                 return (error);
222         }
223
224         /*
225          * Set up the resource manager and the HT->MPIC mapping. For cpcht,
226          * the ranges are properties of the child bridges, and this is also
227          * where we get the HT interrupts properties.
228          */
229
230         bzero(sc->htirq_map, sizeof(sc->htirq_map));
231         for (child = OF_child(node); child != 0; child = OF_peer(child))
232                 cpcht_configure_htbridge(dev, child);
233
234         /* Now make the mapping table available to the MPIC */
235         cpcht_irqmap = sc->htirq_map;
236
237         device_add_child(dev, "pci", device_get_unit(dev));
238
239         return (bus_generic_attach(dev));
240 }
241
242 static void
243 cpcht_configure_htbridge(device_t dev, phandle_t child)
244 {
245         struct cpcht_softc *sc;
246         struct ofw_pci_register pcir;
247         struct cpcht_range ranges[6], *rp;
248         int nranges, ptr, nextptr;
249         uint32_t vend, val;
250         int i, nirq, irq;
251         u_int f, s;
252
253         sc = device_get_softc(dev);
254         if (OF_getprop(child, "reg", &pcir, sizeof(pcir)) == -1)
255                 return;
256
257         s = OFW_PCI_PHYS_HI_DEVICE(pcir.phys_hi);
258         f = OFW_PCI_PHYS_HI_FUNCTION(pcir.phys_hi);
259
260         /*
261          * Mark this slot is populated. The remote south bridge does
262          * not like us talking to unpopulated slots on the root bus.
263          */
264         sc->sc_populated_slots |= (1 << s);
265
266         /*
267          * Next grab this child bus's bus ranges.
268          */
269         bzero(ranges, sizeof(ranges));
270         nranges = OF_getprop(child, "ranges", ranges, sizeof(ranges));
271         
272         ranges[6].pci_hi = 0;
273         for (rp = ranges; rp->pci_hi != 0; rp++) {
274                 switch (rp->pci_hi & OFW_PCI_PHYS_HI_SPACEMASK) {
275                 case OFW_PCI_PHYS_HI_SPACE_CONFIG:
276                         break;
277                 case OFW_PCI_PHYS_HI_SPACE_IO:
278                 case OFW_PCI_PHYS_HI_SPACE_MEM32:
279                         rman_manage_region(&sc->sc_mem_rman, rp->pci_lo,
280                             rp->pci_lo + rp->size_lo - 1);
281                         break;
282                 case OFW_PCI_PHYS_HI_SPACE_MEM64:
283                         panic("64-bit CPCHT reserved memory!");
284                         break;
285                 }
286         }
287
288         /*
289          * Next build up any HT->MPIC mappings for this sub-bus. One would
290          * naively hope that enabling, disabling, and EOIing interrupts would
291          * cause the appropriate HT bus transactions to that effect. This is
292          * not the case.
293          *
294          * Instead, we have to muck about on the HT peer's root PCI bridges,
295          * figure out what interrupts they send, enable them, and cache
296          * the location of their WaitForEOI registers so that we can
297          * send EOIs later.
298          */
299
300         /* All the devices we are interested in have caps */
301         if (!(PCIB_READ_CONFIG(dev, 0, s, f, PCIR_STATUS, 2)
302             & PCIM_STATUS_CAPPRESENT))
303                 return;
304
305         nextptr = PCIB_READ_CONFIG(dev, 0, s, f, PCIR_CAP_PTR, 1);
306         while (nextptr != 0) {
307                 ptr = nextptr;
308                 nextptr = PCIB_READ_CONFIG(dev, 0, s, f,
309                     ptr + PCICAP_NEXTPTR, 1);
310
311                 /* Find the HT IRQ capabilities */
312                 if (PCIB_READ_CONFIG(dev, 0, s, f,
313                     ptr + PCICAP_ID, 1) != PCIY_HT)
314                         continue;
315
316                 val = PCIB_READ_CONFIG(dev, 0, s, f, ptr + PCIR_HT_COMMAND, 2);
317                 if ((val & PCIM_HTCMD_CAP_MASK) != PCIM_HTCAP_INTERRUPT)
318                         continue;
319
320                 /* Ask for the IRQ count */
321                 PCIB_WRITE_CONFIG(dev, 0, s, f, ptr + PCIR_HT_COMMAND, 0x1, 1);
322                 nirq = PCIB_READ_CONFIG(dev, 0, s, f, ptr + 4, 4);
323                 nirq = ((nirq >> 16) & 0xff) + 1;
324
325                 device_printf(dev, "%d HT IRQs on device %d.%d\n", nirq, s, f);
326
327                 for (i = 0; i < nirq; i++) {
328                         PCIB_WRITE_CONFIG(dev, 0, s, f,
329                              ptr + PCIR_HT_COMMAND, 0x10 + (i << 1), 1);
330                         irq = PCIB_READ_CONFIG(dev, 0, s, f, ptr + 4, 4);
331
332                         /*
333                          * Mask this interrupt for now.
334                          */
335                         PCIB_WRITE_CONFIG(dev, 0, s, f, ptr + 4,
336                             irq | HTAPIC_MASK, 4);
337                         irq = (irq >> 16) & 0xff;
338
339                         sc->htirq_map[irq].ht_source = i;
340                         sc->htirq_map[irq].ht_base = sc->sc_data + 
341                             (((((s & 0x1f) << 3) | (f & 0x07)) << 8) | (ptr));
342
343                         PCIB_WRITE_CONFIG(dev, 0, s, f,
344                              ptr + PCIR_HT_COMMAND, 0x11 + (i << 1), 1);
345                         sc->htirq_map[irq].eoi_data =
346                             PCIB_READ_CONFIG(dev, 0, s, f, ptr + 4, 4) |
347                             0x80000000;
348
349                         /*
350                          * Apple uses a non-compliant IO/APIC that differs
351                          * in how we signal EOIs. Check if this device was 
352                          * made by Apple, and act accordingly.
353                          */
354                         vend = PCIB_READ_CONFIG(dev, 0, s, f,
355                             PCIR_DEVVENDOR, 4);
356                         if ((vend & 0xffff) == 0x106b)
357                                 sc->htirq_map[irq].apple_eoi = 
358                                  (sc->htirq_map[irq].ht_base - ptr) + 0x60;
359                 }
360         }
361 }
362
363 static int
364 cpcht_maxslots(device_t dev)
365 {
366
367         return (PCI_SLOTMAX);
368 }
369
370 static u_int32_t
371 cpcht_read_config(device_t dev, u_int bus, u_int slot, u_int func, u_int reg,
372     int width)
373 {
374         struct          cpcht_softc *sc;
375         vm_offset_t     caoff;
376
377         sc = device_get_softc(dev);
378         caoff = sc->sc_data + 
379                 (((((slot & 0x1f) << 3) | (func & 0x07)) << 8) | reg);
380
381         if (bus == 0 && (!(sc->sc_populated_slots & (1 << slot)) || func > 0))
382                 return (0xffffffff);
383
384         if (bus > 0)
385                 caoff += 0x01000000UL + (bus << 16);
386
387         switch (width) {
388         case 1:
389                 return (in8rb(caoff));
390                 break;
391         case 2:
392                 return (in16rb(caoff));
393                 break;
394         case 4:
395                 return (in32rb(caoff));
396                 break;
397         }
398
399         return (0xffffffff);
400 }
401
402 static void
403 cpcht_write_config(device_t dev, u_int bus, u_int slot, u_int func,
404     u_int reg, u_int32_t val, int width)
405 {
406         struct          cpcht_softc *sc;
407         vm_offset_t     caoff;
408
409         sc = device_get_softc(dev);
410         caoff = sc->sc_data + 
411                 (((((slot & 0x1f) << 3) | (func & 0x07)) << 8) | reg);
412
413         if (bus == 0 && (!(sc->sc_populated_slots & (1 << slot)) || func > 0))
414                 return;
415
416         if (bus > 0)
417                 caoff += 0x01000000UL + (bus << 16);
418
419         switch (width) {
420         case 1:
421                 out8rb(caoff, val);
422                 break;
423         case 2:
424                 out16rb(caoff, val);
425                 break;
426         case 4:
427                 out32rb(caoff, val);
428                 break;
429         }
430 }
431
432 static int
433 cpcht_read_ivar(device_t dev, device_t child, int which, uintptr_t *result)
434 {
435         struct  cpcht_softc *sc;
436
437         sc = device_get_softc(dev);
438
439         switch (which) {
440         case PCIB_IVAR_DOMAIN:
441                 *result = device_get_unit(dev);
442                 return (0);
443         case PCIB_IVAR_BUS:
444                 *result = 0;    /* Root bus */
445                 return (0);
446         }
447
448         return (ENOENT);
449 }
450
451 static phandle_t
452 cpcht_get_node(device_t bus, device_t dev)
453 {
454         struct cpcht_softc *sc;
455
456         sc = device_get_softc(bus);
457         /* We only have one child, the PCI bus, which needs our own node. */
458         return (sc->sc_node);
459 }
460
461 static int
462 cpcht_route_interrupt(device_t bus, device_t dev, int pin)
463 {
464         return (pin);
465 }
466
467 static struct resource *
468 cpcht_alloc_resource(device_t bus, device_t child, int type, int *rid,
469     u_long start, u_long end, u_long count, u_int flags)
470 {
471         struct                  cpcht_softc *sc;
472         struct                  resource *rv;
473         struct                  rman *rm;
474         int                     needactivate, err;
475
476         needactivate = flags & RF_ACTIVE;
477         flags &= ~RF_ACTIVE;
478
479         sc = device_get_softc(bus);
480         err = 0;
481
482         switch (type) {
483         case SYS_RES_IOPORT:
484                 end = min(end, start + count);
485
486                 /* FALLTHROUGH */
487         case SYS_RES_MEMORY:
488                 rm = &sc->sc_mem_rman;
489                 break;
490
491         case SYS_RES_IRQ:
492                 return (bus_alloc_resource(bus, type, rid, start, end, count,
493                     flags));
494
495         default:
496                 device_printf(bus, "unknown resource request from %s\n",
497                     device_get_nameunit(child));
498                 return (NULL);
499         }
500
501         rv = rman_reserve_resource(rm, start, end, count, flags, child);
502         if (rv == NULL) {
503                 device_printf(bus, "failed to reserve resource for %s\n",
504                     device_get_nameunit(child));
505                 return (NULL);
506         }
507
508         rman_set_rid(rv, *rid);
509
510         if (needactivate) {
511                 if (bus_activate_resource(child, type, *rid, rv) != 0) {
512                         device_printf(bus,
513                             "failed to activate resource for %s\n",
514                             device_get_nameunit(child));
515                         rman_release_resource(rv);
516                         return (NULL);
517                 }
518         }
519
520         return (rv);
521 }
522
523 static int
524 cpcht_activate_resource(device_t bus, device_t child, int type, int rid,
525     struct resource *res)
526 {
527         void    *p;
528         struct  cpcht_softc *sc;
529
530         sc = device_get_softc(bus);
531
532         if (type == SYS_RES_IRQ)
533                 return (bus_activate_resource(bus, type, rid, res));
534
535         if (type == SYS_RES_MEMORY || type == SYS_RES_IOPORT) {
536                 vm_offset_t start;
537
538                 start = (vm_offset_t)rman_get_start(res);
539
540                 if (bootverbose)
541                         printf("cpcht mapdev: start %zx, len %ld\n", start,
542                             rman_get_size(res));
543
544                 p = pmap_mapdev(start, (vm_size_t)rman_get_size(res));
545                 if (p == NULL)
546                         return (ENOMEM);
547                 rman_set_virtual(res, p);
548                 rman_set_bustag(res, &bs_le_tag);
549                 rman_set_bushandle(res, (u_long)p);
550         }
551
552         return (rman_activate_resource(res));
553 }
554
555 static int
556 cpcht_release_resource(device_t bus, device_t child, int type, int rid,
557     struct resource *res)
558 {
559
560         if (rman_get_flags(res) & RF_ACTIVE) {
561                 int error = bus_deactivate_resource(child, type, rid, res);
562                 if (error)
563                         return error;
564         }
565
566         return (rman_release_resource(res));
567 }
568
569 static int
570 cpcht_deactivate_resource(device_t bus, device_t child, int type, int rid,
571     struct resource *res)
572 {
573
574         /*
575          * If this is a memory resource, unmap it.
576          */
577         if ((type == SYS_RES_MEMORY) || (type == SYS_RES_IOPORT)) {
578                 u_int32_t psize;
579
580                 psize = rman_get_size(res);
581                 pmap_unmapdev((vm_offset_t)rman_get_virtual(res), psize);
582         }
583
584         return (rman_deactivate_resource(res));
585 }
586
587 /*
588  * Driver for the integrated MPIC on U3/U4 (CPC925/CPC945)
589  */
590
591 static int      openpic_cpcht_probe(device_t);
592 static int      openpic_cpcht_attach(device_t);
593 static void     openpic_cpcht_config(device_t, u_int irq,
594                     enum intr_trigger trig, enum intr_polarity pol);
595 static void     openpic_cpcht_enable(device_t, u_int irq, u_int vector);
596 static void     openpic_cpcht_unmask(device_t, u_int irq);
597 static void     openpic_cpcht_eoi(device_t, u_int irq);
598
599 static device_method_t  openpic_cpcht_methods[] = {
600         /* Device interface */
601         DEVMETHOD(device_probe,         openpic_cpcht_probe),
602         DEVMETHOD(device_attach,        openpic_cpcht_attach),
603
604         /* PIC interface */
605         DEVMETHOD(pic_config,           openpic_cpcht_config),
606         DEVMETHOD(pic_dispatch,         openpic_dispatch),
607         DEVMETHOD(pic_enable,           openpic_cpcht_enable),
608         DEVMETHOD(pic_eoi,              openpic_cpcht_eoi),
609         DEVMETHOD(pic_ipi,              openpic_ipi),
610         DEVMETHOD(pic_mask,             openpic_mask),
611         DEVMETHOD(pic_unmask,           openpic_cpcht_unmask),
612
613         { 0, 0 },
614 };
615
616 struct openpic_cpcht_softc {
617         struct openpic_softc sc_openpic;
618
619         struct mtx sc_ht_mtx;
620 };
621
622 static driver_t openpic_cpcht_driver = {
623         "htpic",
624         openpic_cpcht_methods,
625         sizeof(struct openpic_cpcht_softc),
626 };
627
628 DRIVER_MODULE(openpic, unin, openpic_cpcht_driver, openpic_devclass, 0, 0);
629
630 static int
631 openpic_cpcht_probe(device_t dev)
632 {
633         const char *type = ofw_bus_get_type(dev);
634
635         if (strcmp(type, "open-pic") != 0)
636                 return (ENXIO);
637
638         device_set_desc(dev, OPENPIC_DEVSTR);
639         return (0);
640 }
641
642 static int
643 openpic_cpcht_attach(device_t dev)
644 {
645         struct openpic_cpcht_softc *sc;
646         int err, irq;
647
648         err = openpic_attach(dev);
649         if (err != 0)
650                 return (err);
651
652         /*
653          * The HT APIC stuff is not thread-safe, so we need a mutex to
654          * protect it.
655          */
656         sc = device_get_softc(dev);
657         mtx_init(&sc->sc_ht_mtx, "htpic", NULL, MTX_SPIN);
658
659         /*
660          * Interrupts 0-3 are internally sourced and are level triggered
661          * active low. Interrupts 4-123 are connected to a pulse generator
662          * and should be programmed as edge triggered low-to-high.
663          * 
664          * IBM CPC945 Manual, Section 9.3.
665          */
666
667         for (irq = 0; irq < 4; irq++)
668                 openpic_config(dev, irq, INTR_TRIGGER_LEVEL, INTR_POLARITY_LOW);
669         for (irq = 4; irq < 124; irq++)
670                 openpic_config(dev, irq, INTR_TRIGGER_EDGE, INTR_POLARITY_LOW);
671
672         return (0);
673 }
674
675 static void
676 openpic_cpcht_config(device_t dev, u_int irq, enum intr_trigger trig,
677     enum intr_polarity pol)
678 {
679         struct openpic_cpcht_softc *sc;
680         uint32_t ht_irq;
681
682         /*
683          * The interrupt settings for the MPIC are completely determined
684          * by the internal wiring in the northbridge. Real changes to these
685          * settings need to be negotiated with the remote IO-APIC on the HT
686          * link.
687          */
688
689         sc = device_get_softc(dev);
690
691         if (cpcht_irqmap != NULL && irq < 128 &&
692             cpcht_irqmap[irq].ht_base > 0 && !cpcht_irqmap[irq].edge) {
693                 mtx_lock_spin(&sc->sc_ht_mtx);
694
695                 /* Program the data port */
696                 out8rb(cpcht_irqmap[irq].ht_base + PCIR_HT_COMMAND,
697                     0x10 + (cpcht_irqmap[irq].ht_source << 1));
698
699                 /* Grab the IRQ config register */
700                 ht_irq = in32rb(cpcht_irqmap[irq].ht_base + 4);
701
702                 /* Mask the IRQ while we fiddle settings */
703                 out32rb(cpcht_irqmap[irq].ht_base + 4, ht_irq | HTAPIC_MASK);
704                 
705                 /* Program the interrupt sense */
706                 ht_irq &= ~(HTAPIC_TRIGGER_LEVEL | HTAPIC_REQUEST_EOI);
707                 if (trig == INTR_TRIGGER_EDGE) {
708                         cpcht_irqmap[irq].edge = 1;
709                 } else {
710                         cpcht_irqmap[irq].edge = 0;
711                         ht_irq |= HTAPIC_TRIGGER_LEVEL | HTAPIC_REQUEST_EOI;
712                 }
713                 out32rb(cpcht_irqmap[irq].ht_base + 4, ht_irq);
714
715                 mtx_unlock_spin(&sc->sc_ht_mtx);
716         }
717 }
718
719 static void
720 openpic_cpcht_enable(device_t dev, u_int irq, u_int vec)
721 {
722         struct openpic_cpcht_softc *sc;
723         uint32_t ht_irq;
724
725         openpic_enable(dev, irq, vec);
726
727         sc = device_get_softc(dev);
728
729         if (cpcht_irqmap != NULL && irq < 128 &&
730             cpcht_irqmap[irq].ht_base > 0) {
731                 mtx_lock_spin(&sc->sc_ht_mtx);
732
733                 /* Program the data port */
734                 out8rb(cpcht_irqmap[irq].ht_base + PCIR_HT_COMMAND,
735                     0x10 + (cpcht_irqmap[irq].ht_source << 1));
736
737                 /* Unmask the interrupt */
738                 ht_irq = in32rb(cpcht_irqmap[irq].ht_base + 4);
739                 ht_irq &= ~HTAPIC_MASK;
740                 out32rb(cpcht_irqmap[irq].ht_base + 4, ht_irq);
741
742                 mtx_unlock_spin(&sc->sc_ht_mtx);
743         }
744                 
745         openpic_cpcht_eoi(dev, irq);
746 }
747
748 static void
749 openpic_cpcht_unmask(device_t dev, u_int irq)
750 {
751         struct openpic_cpcht_softc *sc;
752         uint32_t ht_irq;
753
754         openpic_unmask(dev, irq);
755
756         sc = device_get_softc(dev);
757
758         if (cpcht_irqmap != NULL && irq < 128 &&
759             cpcht_irqmap[irq].ht_base > 0) {
760                 mtx_lock_spin(&sc->sc_ht_mtx);
761
762                 /* Program the data port */
763                 out8rb(cpcht_irqmap[irq].ht_base + PCIR_HT_COMMAND,
764                     0x10 + (cpcht_irqmap[irq].ht_source << 1));
765
766                 /* Unmask the interrupt */
767                 ht_irq = in32rb(cpcht_irqmap[irq].ht_base + 4);
768                 ht_irq &= ~HTAPIC_MASK;
769                 out32rb(cpcht_irqmap[irq].ht_base + 4, ht_irq);
770
771                 mtx_unlock_spin(&sc->sc_ht_mtx);
772         }
773
774         openpic_cpcht_eoi(dev, irq);
775 }
776
777 static void
778 openpic_cpcht_eoi(device_t dev, u_int irq)
779 {
780         struct openpic_cpcht_softc *sc;
781         uint32_t off, mask;
782
783         if (irq == 255)
784                 return;
785
786         sc = device_get_softc(dev);
787
788         if (cpcht_irqmap != NULL && irq < 128 &&
789             cpcht_irqmap[irq].ht_base > 0 && !cpcht_irqmap[irq].edge) {
790                 /* If this is an HT IRQ, acknowledge it at the remote APIC */
791
792                 if (cpcht_irqmap[irq].apple_eoi) {
793                         off = (cpcht_irqmap[irq].ht_source >> 3) & ~3;
794                         mask = 1 << (cpcht_irqmap[irq].ht_source & 0x1f);
795                         out32rb(cpcht_irqmap[irq].apple_eoi + off, mask);
796                 } else {
797                         mtx_lock_spin(&sc->sc_ht_mtx);
798
799                         out8rb(cpcht_irqmap[irq].ht_base + PCIR_HT_COMMAND,
800                             0x11 + (cpcht_irqmap[irq].ht_source << 1));
801                         out32rb(cpcht_irqmap[irq].ht_base + 4,
802                             cpcht_irqmap[irq].eoi_data);
803
804                         mtx_unlock_spin(&sc->sc_ht_mtx);
805                 }
806         }
807
808         openpic_eoi(dev, irq);
809 }
810