]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - sys/powerpc/powermac/cpcht.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.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
26 #include <sys/cdefs.h>
27 __FBSDID("$FreeBSD$");
28
29 #include <sys/param.h>
30 #include <sys/systm.h>
31 #include <sys/module.h>
32 #include <sys/bus.h>
33 #include <sys/conf.h>
34 #include <sys/kernel.h>
35 #include <sys/pciio.h>
36 #include <sys/rman.h>
37
38 #include <dev/ofw/openfirm.h>
39 #include <dev/ofw/ofw_pci.h>
40
41 #include <dev/pci/pcivar.h>
42 #include <dev/pci/pcireg.h>
43
44 #include <machine/bus.h>
45 #include <machine/intr_machdep.h>
46 #include <machine/md_var.h>
47 #include <machine/openpicvar.h>
48 #include <machine/pio.h>
49 #include <machine/resource.h>
50
51 #include <dev/ofw/ofw_bus.h>
52 #include <dev/ofw/ofw_bus_subr.h>
53 #include <powerpc/ofw/ofw_pci.h>
54
55 #include <vm/vm.h>
56 #include <vm/pmap.h>
57
58 #include "pcib_if.h"
59 #include "pic_if.h"
60
61 /*
62  * IBM CPC9X5 Hypertransport Device interface.
63  */
64 static int              cpcht_probe(device_t);
65 static int              cpcht_attach(device_t);
66
67 static void             cpcht_configure_htbridge(device_t, phandle_t);
68
69 /*
70  * pcib interface.
71  */
72 static u_int32_t        cpcht_read_config(device_t, u_int, u_int, u_int,
73                             u_int, int);
74 static void             cpcht_write_config(device_t, u_int, u_int, u_int,
75                             u_int, u_int32_t, int);
76 static int              cpcht_route_interrupt(device_t, device_t, int);
77 static int              cpcht_alloc_msi(device_t dev, device_t child,
78                             int count, int maxcount, int *irqs);
79 static int              cpcht_release_msi(device_t dev, device_t child,
80                             int count, int *irqs);
81 static int              cpcht_alloc_msix(device_t dev, device_t child,
82                             int *irq);
83 static int              cpcht_release_msix(device_t dev, device_t child,
84                             int irq);
85 static int              cpcht_map_msi(device_t dev, device_t child,
86                             int irq, uint64_t *addr, uint32_t *data);
87
88 /*
89  * Driver methods.
90  */
91 static device_method_t  cpcht_methods[] = {
92         /* Device interface */
93         DEVMETHOD(device_probe,         cpcht_probe),
94         DEVMETHOD(device_attach,        cpcht_attach),
95
96         /* pcib interface */
97         DEVMETHOD(pcib_read_config,     cpcht_read_config),
98         DEVMETHOD(pcib_write_config,    cpcht_write_config),
99         DEVMETHOD(pcib_route_interrupt, cpcht_route_interrupt),
100         DEVMETHOD(pcib_alloc_msi,       cpcht_alloc_msi),
101         DEVMETHOD(pcib_release_msi,     cpcht_release_msi),
102         DEVMETHOD(pcib_alloc_msix,      cpcht_alloc_msix),
103         DEVMETHOD(pcib_release_msix,    cpcht_release_msix),
104         DEVMETHOD(pcib_map_msi,         cpcht_map_msi),
105
106         DEVMETHOD_END
107 };
108
109 struct cpcht_irq {
110         enum {
111             IRQ_NONE, IRQ_HT, IRQ_MSI, IRQ_INTERNAL
112         }               irq_type; 
113
114         int             ht_source;
115
116         vm_offset_t     ht_base;
117         vm_offset_t     apple_eoi;
118         uint32_t        eoi_data;
119         int             edge;
120 };
121
122 static struct cpcht_irq *cpcht_irqmap = NULL;
123 uint32_t cpcht_msipic = 0;
124
125 struct cpcht_softc {
126         struct ofw_pci_softc    pci_sc;
127         vm_offset_t             sc_data;
128         uint64_t                sc_populated_slots;
129
130         struct cpcht_irq        htirq_map[128];
131         struct mtx              htirq_mtx;
132 };
133
134 static devclass_t       cpcht_devclass;
135 DEFINE_CLASS_1(pcib, cpcht_driver, cpcht_methods, sizeof(struct cpcht_softc),
136     ofw_pci_driver);
137 DRIVER_MODULE(cpcht, nexus, cpcht_driver, cpcht_devclass, 0, 0);
138
139 #define CPCHT_IOPORT_BASE       0xf4000000UL /* Hardwired */
140 #define CPCHT_IOPORT_SIZE       0x00400000UL
141
142 #define HTAPIC_REQUEST_EOI      0x20
143 #define HTAPIC_TRIGGER_LEVEL    0x02
144 #define HTAPIC_MASK             0x01
145
146 static int
147 cpcht_probe(device_t dev)
148 {
149         const char      *type, *compatible;
150
151         type = ofw_bus_get_type(dev);
152         compatible = ofw_bus_get_compat(dev);
153
154         if (type == NULL || compatible == NULL)
155                 return (ENXIO);
156
157         if (strcmp(type, "ht") != 0)
158                 return (ENXIO);
159
160         if (strcmp(compatible, "u3-ht") != 0)
161                 return (ENXIO);
162
163         device_set_desc(dev, "IBM CPC9X5 HyperTransport Tunnel");
164         return (0);
165 }
166
167 static int
168 cpcht_attach(device_t dev)
169 {
170         struct          cpcht_softc *sc;
171         phandle_t       node, child;
172         u_int32_t       reg[3];
173         int             i;
174
175         node = ofw_bus_get_node(dev);
176         sc = device_get_softc(dev);
177
178         if (OF_getprop(node, "reg", reg, sizeof(reg)) < 12)
179                 return (ENXIO);
180
181         if (OF_getproplen(node, "ranges") <= 0)
182                 sc->pci_sc.sc_quirks = OFW_PCI_QUIRK_RANGES_ON_CHILDREN;
183         sc->sc_populated_slots = 0;
184         sc->sc_data = (vm_offset_t)pmap_mapdev(reg[1], reg[2]);
185
186         /*
187          * Set up the resource manager and the HT->MPIC mapping. For cpcht,
188          * the ranges are properties of the child bridges, and this is also
189          * where we get the HT interrupts properties.
190          */
191
192 #if 0
193         /* I/O port mappings are usually not in the device tree */
194         rman_manage_region(&sc->pci_sc.sc_io_rman, 0, CPCHT_IOPORT_SIZE - 1);
195 #endif
196
197         bzero(sc->htirq_map, sizeof(sc->htirq_map));
198         mtx_init(&sc->htirq_mtx, "cpcht irq", NULL, MTX_DEF);
199         for (i = 0; i < 8; i++)
200                 sc->htirq_map[i].irq_type = IRQ_INTERNAL;
201         for (child = OF_child(node); child != 0; child = OF_peer(child))
202                 cpcht_configure_htbridge(dev, child);
203
204         /* Now make the mapping table available to the MPIC */
205         cpcht_irqmap = sc->htirq_map;
206
207         return (ofw_pci_attach(dev));
208 }
209
210 static void
211 cpcht_configure_htbridge(device_t dev, phandle_t child)
212 {
213         struct cpcht_softc *sc;
214         struct ofw_pci_register pcir;
215         int ptr, nextptr;
216         uint32_t vend, val;
217         int i, nirq, irq;
218         u_int b, f, s;
219
220         sc = device_get_softc(dev);
221         if (OF_getprop(child, "reg", &pcir, sizeof(pcir)) == -1)
222                 return;
223
224         b = OFW_PCI_PHYS_HI_BUS(pcir.phys_hi);
225         s = OFW_PCI_PHYS_HI_DEVICE(pcir.phys_hi);
226         f = OFW_PCI_PHYS_HI_FUNCTION(pcir.phys_hi);
227
228         /*
229          * Mark this slot is populated. The remote south bridge does
230          * not like us talking to unpopulated slots on the root bus.
231          */
232         sc->sc_populated_slots |= (1 << s);
233
234         /*
235          * Next build up any HT->MPIC mappings for this sub-bus. One would
236          * naively hope that enabling, disabling, and EOIing interrupts would
237          * cause the appropriate HT bus transactions to that effect. This is
238          * not the case.
239          *
240          * Instead, we have to muck about on the HT peer's root PCI bridges,
241          * figure out what interrupts they send, enable them, and cache
242          * the location of their WaitForEOI registers so that we can
243          * send EOIs later.
244          */
245
246         /* All the devices we are interested in have caps */
247         if (!(PCIB_READ_CONFIG(dev, b, s, f, PCIR_STATUS, 2)
248             & PCIM_STATUS_CAPPRESENT))
249                 return;
250
251         nextptr = PCIB_READ_CONFIG(dev, b, s, f, PCIR_CAP_PTR, 1);
252         while (nextptr != 0) {
253                 ptr = nextptr;
254                 nextptr = PCIB_READ_CONFIG(dev, b, s, f,
255                     ptr + PCICAP_NEXTPTR, 1);
256
257                 /* Find the HT IRQ capabilities */
258                 if (PCIB_READ_CONFIG(dev, b, s, f,
259                     ptr + PCICAP_ID, 1) != PCIY_HT)
260                         continue;
261
262                 val = PCIB_READ_CONFIG(dev, b, s, f, ptr + PCIR_HT_COMMAND, 2);
263                 if ((val & PCIM_HTCMD_CAP_MASK) != PCIM_HTCAP_INTERRUPT)
264                         continue;
265
266                 /* Ask for the IRQ count */
267                 PCIB_WRITE_CONFIG(dev, b, s, f, ptr + PCIR_HT_COMMAND, 0x1, 1);
268                 nirq = PCIB_READ_CONFIG(dev, b, s, f, ptr + 4, 4);
269                 nirq = ((nirq >> 16) & 0xff) + 1;
270
271                 device_printf(dev, "%d HT IRQs on device %d.%d\n", nirq, s, f);
272
273                 for (i = 0; i < nirq; i++) {
274                         PCIB_WRITE_CONFIG(dev, b, s, f,
275                              ptr + PCIR_HT_COMMAND, 0x10 + (i << 1), 1);
276                         irq = PCIB_READ_CONFIG(dev, b, s, f, ptr + 4, 4);
277
278                         /*
279                          * Mask this interrupt for now.
280                          */
281                         PCIB_WRITE_CONFIG(dev, b, s, f, ptr + 4,
282                             irq | HTAPIC_MASK, 4);
283                         irq = (irq >> 16) & 0xff;
284
285                         sc->htirq_map[irq].irq_type = IRQ_HT;
286                         sc->htirq_map[irq].ht_source = i;
287                         sc->htirq_map[irq].ht_base = sc->sc_data + 
288                             (((((s & 0x1f) << 3) | (f & 0x07)) << 8) | (ptr));
289
290                         PCIB_WRITE_CONFIG(dev, b, s, f,
291                              ptr + PCIR_HT_COMMAND, 0x11 + (i << 1), 1);
292                         sc->htirq_map[irq].eoi_data =
293                             PCIB_READ_CONFIG(dev, b, s, f, ptr + 4, 4) |
294                             0x80000000;
295
296                         /*
297                          * Apple uses a non-compliant IO/APIC that differs
298                          * in how we signal EOIs. Check if this device was 
299                          * made by Apple, and act accordingly.
300                          */
301                         vend = PCIB_READ_CONFIG(dev, b, s, f,
302                             PCIR_DEVVENDOR, 4);
303                         if ((vend & 0xffff) == 0x106b)
304                                 sc->htirq_map[irq].apple_eoi = 
305                                  (sc->htirq_map[irq].ht_base - ptr) + 0x60;
306                 }
307         }
308 }
309
310 static u_int32_t
311 cpcht_read_config(device_t dev, u_int bus, u_int slot, u_int func, u_int reg,
312     int width)
313 {
314         struct          cpcht_softc *sc;
315         vm_offset_t     caoff;
316
317         sc = device_get_softc(dev);
318         caoff = sc->sc_data + 
319                 (((((slot & 0x1f) << 3) | (func & 0x07)) << 8) | reg);
320
321         if (bus == 0 && (!(sc->sc_populated_slots & (1 << slot)) || func > 0))
322                 return (0xffffffff);
323
324         if (bus > 0)
325                 caoff += 0x01000000UL + (bus << 16);
326
327         switch (width) {
328         case 1:
329                 return (in8rb(caoff));
330                 break;
331         case 2:
332                 return (in16rb(caoff));
333                 break;
334         case 4:
335                 return (in32rb(caoff));
336                 break;
337         }
338
339         return (0xffffffff);
340 }
341
342 static void
343 cpcht_write_config(device_t dev, u_int bus, u_int slot, u_int func,
344     u_int reg, u_int32_t val, int width)
345 {
346         struct          cpcht_softc *sc;
347         vm_offset_t     caoff;
348
349         sc = device_get_softc(dev);
350         caoff = sc->sc_data + 
351                 (((((slot & 0x1f) << 3) | (func & 0x07)) << 8) | reg);
352
353         if (bus == 0 && (!(sc->sc_populated_slots & (1 << slot)) || func > 0))
354                 return;
355
356         if (bus > 0)
357                 caoff += 0x01000000UL + (bus << 16);
358
359         switch (width) {
360         case 1:
361                 out8rb(caoff, val);
362                 break;
363         case 2:
364                 out16rb(caoff, val);
365                 break;
366         case 4:
367                 out32rb(caoff, val);
368                 break;
369         }
370 }
371
372 static int
373 cpcht_route_interrupt(device_t bus, device_t dev, int pin)
374 {
375         return (pin);
376 }
377
378 static int
379 cpcht_alloc_msi(device_t dev, device_t child, int count, int maxcount,
380     int *irqs)
381 {
382         struct cpcht_softc *sc;
383         int i, j;
384
385         sc = device_get_softc(dev);
386         j = 0;
387
388         /* Bail if no MSI PIC yet */
389         if (cpcht_msipic == 0)
390                 return (ENXIO);
391
392         mtx_lock(&sc->htirq_mtx);
393         for (i = 8; i < 124 - count; i++) {
394                 for (j = 0; j < count; j++) {
395                         if (sc->htirq_map[i+j].irq_type != IRQ_NONE)
396                                 break;
397                 }
398                 if (j == count)
399                         break;
400
401                 i += j; /* We know there isn't a large enough run */
402         }
403
404         if (j != count) {
405                 mtx_unlock(&sc->htirq_mtx);
406                 return (ENXIO);
407         }
408
409         for (j = 0; j < count; j++) {
410                 irqs[j] = MAP_IRQ(cpcht_msipic, i+j);
411                 sc->htirq_map[i+j].irq_type = IRQ_MSI;
412         }
413         mtx_unlock(&sc->htirq_mtx);
414
415         return (0);
416 }
417
418 static int
419 cpcht_release_msi(device_t dev, device_t child, int count, int *irqs)
420 {
421         struct cpcht_softc *sc;
422         int i;
423
424         sc = device_get_softc(dev);
425
426         mtx_lock(&sc->htirq_mtx);
427         for (i = 0; i < count; i++)
428                 sc->htirq_map[irqs[i] & 0xff].irq_type = IRQ_NONE;
429         mtx_unlock(&sc->htirq_mtx);
430
431         return (0);
432 }
433
434 static int
435 cpcht_alloc_msix(device_t dev, device_t child, int *irq)
436 {
437         struct cpcht_softc *sc;
438         int i;
439
440         sc = device_get_softc(dev);
441
442         /* Bail if no MSI PIC yet */
443         if (cpcht_msipic == 0)
444                 return (ENXIO);
445
446         mtx_lock(&sc->htirq_mtx);
447         for (i = 8; i < 124; i++) {
448                 if (sc->htirq_map[i].irq_type == IRQ_NONE) {
449                         sc->htirq_map[i].irq_type = IRQ_MSI;
450                         *irq = MAP_IRQ(cpcht_msipic, i);
451
452                         mtx_unlock(&sc->htirq_mtx);
453                         return (0);
454                 }
455         }
456         mtx_unlock(&sc->htirq_mtx);
457
458         return (ENXIO);
459 }
460         
461 static int
462 cpcht_release_msix(device_t dev, device_t child, int irq)
463 {
464         struct cpcht_softc *sc;
465
466         sc = device_get_softc(dev);
467
468         mtx_lock(&sc->htirq_mtx);
469         sc->htirq_map[irq & 0xff].irq_type = IRQ_NONE;
470         mtx_unlock(&sc->htirq_mtx);
471
472         return (0);
473 }
474
475 static int
476 cpcht_map_msi(device_t dev, device_t child, int irq, uint64_t *addr,
477     uint32_t *data)
478 {
479         device_t pcib;
480         struct pci_devinfo *dinfo;
481         struct pcicfg_ht *ht = NULL;
482
483         for (pcib = child; pcib != dev; pcib =
484             device_get_parent(device_get_parent(pcib))) {
485                 dinfo = device_get_ivars(pcib);
486                 ht = &dinfo->cfg.ht;
487
488                 if (ht == NULL)
489                         continue;
490         }
491
492         if (ht == NULL)
493                 return (ENXIO);
494
495         *addr = ht->ht_msiaddr;
496         *data = irq & 0xff;
497
498         return (0);
499 }
500
501 /*
502  * Driver for the integrated MPIC on U3/U4 (CPC925/CPC945)
503  */
504
505 static int      openpic_cpcht_probe(device_t);
506 static int      openpic_cpcht_attach(device_t);
507 static void     openpic_cpcht_config(device_t, u_int irq,
508                     enum intr_trigger trig, enum intr_polarity pol);
509 static void     openpic_cpcht_enable(device_t, u_int irq, u_int vector);
510 static void     openpic_cpcht_unmask(device_t, u_int irq);
511 static void     openpic_cpcht_eoi(device_t, u_int irq);
512
513 static device_method_t  openpic_cpcht_methods[] = {
514         /* Device interface */
515         DEVMETHOD(device_probe,         openpic_cpcht_probe),
516         DEVMETHOD(device_attach,        openpic_cpcht_attach),
517
518         /* PIC interface */
519         DEVMETHOD(pic_bind,             openpic_bind),
520         DEVMETHOD(pic_config,           openpic_cpcht_config),
521         DEVMETHOD(pic_dispatch,         openpic_dispatch),
522         DEVMETHOD(pic_enable,           openpic_cpcht_enable),
523         DEVMETHOD(pic_eoi,              openpic_cpcht_eoi),
524         DEVMETHOD(pic_ipi,              openpic_ipi),
525         DEVMETHOD(pic_mask,             openpic_mask),
526         DEVMETHOD(pic_unmask,           openpic_cpcht_unmask),
527
528         { 0, 0 },
529 };
530
531 struct openpic_cpcht_softc {
532         struct openpic_softc sc_openpic;
533
534         struct mtx sc_ht_mtx;
535 };
536
537 static driver_t openpic_cpcht_driver = {
538         "htpic",
539         openpic_cpcht_methods,
540         sizeof(struct openpic_cpcht_softc),
541 };
542
543 DRIVER_MODULE(openpic, unin, openpic_cpcht_driver, openpic_devclass, 0, 0);
544
545 static int
546 openpic_cpcht_probe(device_t dev)
547 {
548         const char *type = ofw_bus_get_type(dev);
549
550         if (strcmp(type, "open-pic") != 0)
551                 return (ENXIO);
552
553         device_set_desc(dev, OPENPIC_DEVSTR);
554         return (0);
555 }
556
557 static int
558 openpic_cpcht_attach(device_t dev)
559 {
560         struct openpic_cpcht_softc *sc;
561         phandle_t node;
562         int err, irq;
563
564         node = ofw_bus_get_node(dev);
565         err = openpic_common_attach(dev, node);
566         if (err != 0)
567                 return (err);
568
569         /*
570          * The HT APIC stuff is not thread-safe, so we need a mutex to
571          * protect it.
572          */
573         sc = device_get_softc(dev);
574         mtx_init(&sc->sc_ht_mtx, "htpic", NULL, MTX_SPIN);
575
576         /*
577          * Interrupts 0-3 are internally sourced and are level triggered
578          * active low. Interrupts 4-123 are connected to a pulse generator
579          * and should be programmed as edge triggered low-to-high.
580          * 
581          * IBM CPC945 Manual, Section 9.3.
582          */
583
584         for (irq = 0; irq < 4; irq++)
585                 openpic_config(dev, irq, INTR_TRIGGER_LEVEL, INTR_POLARITY_LOW);
586         for (irq = 4; irq < 124; irq++)
587                 openpic_config(dev, irq, INTR_TRIGGER_EDGE, INTR_POLARITY_LOW);
588
589         /*
590          * Use this PIC for MSI only if it is the root PIC. This may not
591          * be necessary, but Linux does it, and I cannot find any U3 machines
592          * with MSI devices to test.
593          */
594         if (dev == root_pic)
595                 cpcht_msipic = node;
596
597         return (0);
598 }
599
600 static void
601 openpic_cpcht_config(device_t dev, u_int irq, enum intr_trigger trig,
602     enum intr_polarity pol)
603 {
604         struct openpic_cpcht_softc *sc;
605         uint32_t ht_irq;
606
607         /*
608          * The interrupt settings for the MPIC are completely determined
609          * by the internal wiring in the northbridge. Real changes to these
610          * settings need to be negotiated with the remote IO-APIC on the HT
611          * link.
612          */
613
614         sc = device_get_softc(dev);
615
616         if (cpcht_irqmap != NULL && irq < 128 &&
617             cpcht_irqmap[irq].ht_base > 0 && !cpcht_irqmap[irq].edge) {
618                 mtx_lock_spin(&sc->sc_ht_mtx);
619
620                 /* Program the data port */
621                 out8rb(cpcht_irqmap[irq].ht_base + PCIR_HT_COMMAND,
622                     0x10 + (cpcht_irqmap[irq].ht_source << 1));
623
624                 /* Grab the IRQ config register */
625                 ht_irq = in32rb(cpcht_irqmap[irq].ht_base + 4);
626
627                 /* Mask the IRQ while we fiddle settings */
628                 out32rb(cpcht_irqmap[irq].ht_base + 4, ht_irq | HTAPIC_MASK);
629                 
630                 /* Program the interrupt sense */
631                 ht_irq &= ~(HTAPIC_TRIGGER_LEVEL | HTAPIC_REQUEST_EOI);
632                 if (trig == INTR_TRIGGER_EDGE) {
633                         cpcht_irqmap[irq].edge = 1;
634                 } else {
635                         cpcht_irqmap[irq].edge = 0;
636                         ht_irq |= HTAPIC_TRIGGER_LEVEL | HTAPIC_REQUEST_EOI;
637                 }
638                 out32rb(cpcht_irqmap[irq].ht_base + 4, ht_irq);
639
640                 mtx_unlock_spin(&sc->sc_ht_mtx);
641         }
642 }
643
644 static void
645 openpic_cpcht_enable(device_t dev, u_int irq, u_int vec)
646 {
647         struct openpic_cpcht_softc *sc;
648         uint32_t ht_irq;
649
650         openpic_enable(dev, irq, vec);
651
652         sc = device_get_softc(dev);
653
654         if (cpcht_irqmap != NULL && irq < 128 &&
655             cpcht_irqmap[irq].ht_base > 0) {
656                 mtx_lock_spin(&sc->sc_ht_mtx);
657
658                 /* Program the data port */
659                 out8rb(cpcht_irqmap[irq].ht_base + PCIR_HT_COMMAND,
660                     0x10 + (cpcht_irqmap[irq].ht_source << 1));
661
662                 /* Unmask the interrupt */
663                 ht_irq = in32rb(cpcht_irqmap[irq].ht_base + 4);
664                 ht_irq &= ~HTAPIC_MASK;
665                 out32rb(cpcht_irqmap[irq].ht_base + 4, ht_irq);
666
667                 mtx_unlock_spin(&sc->sc_ht_mtx);
668         }
669                 
670         openpic_cpcht_eoi(dev, irq);
671 }
672
673 static void
674 openpic_cpcht_unmask(device_t dev, u_int irq)
675 {
676         struct openpic_cpcht_softc *sc;
677         uint32_t ht_irq;
678
679         openpic_unmask(dev, irq);
680
681         sc = device_get_softc(dev);
682
683         if (cpcht_irqmap != NULL && irq < 128 &&
684             cpcht_irqmap[irq].ht_base > 0) {
685                 mtx_lock_spin(&sc->sc_ht_mtx);
686
687                 /* Program the data port */
688                 out8rb(cpcht_irqmap[irq].ht_base + PCIR_HT_COMMAND,
689                     0x10 + (cpcht_irqmap[irq].ht_source << 1));
690
691                 /* Unmask the interrupt */
692                 ht_irq = in32rb(cpcht_irqmap[irq].ht_base + 4);
693                 ht_irq &= ~HTAPIC_MASK;
694                 out32rb(cpcht_irqmap[irq].ht_base + 4, ht_irq);
695
696                 mtx_unlock_spin(&sc->sc_ht_mtx);
697         }
698
699         openpic_cpcht_eoi(dev, irq);
700 }
701
702 static void
703 openpic_cpcht_eoi(device_t dev, u_int irq)
704 {
705         struct openpic_cpcht_softc *sc;
706         uint32_t off, mask;
707
708         if (irq == 255)
709                 return;
710
711         sc = device_get_softc(dev);
712
713         if (cpcht_irqmap != NULL && irq < 128 &&
714             cpcht_irqmap[irq].ht_base > 0 && !cpcht_irqmap[irq].edge) {
715                 /* If this is an HT IRQ, acknowledge it at the remote APIC */
716
717                 if (cpcht_irqmap[irq].apple_eoi) {
718                         off = (cpcht_irqmap[irq].ht_source >> 3) & ~3;
719                         mask = 1 << (cpcht_irqmap[irq].ht_source & 0x1f);
720                         out32rb(cpcht_irqmap[irq].apple_eoi + off, mask);
721                 } else {
722                         mtx_lock_spin(&sc->sc_ht_mtx);
723
724                         out8rb(cpcht_irqmap[irq].ht_base + PCIR_HT_COMMAND,
725                             0x11 + (cpcht_irqmap[irq].ht_source << 1));
726                         out32rb(cpcht_irqmap[irq].ht_base + 4,
727                             cpcht_irqmap[irq].eoi_data);
728
729                         mtx_unlock_spin(&sc->sc_ht_mtx);
730                 }
731         }
732
733         openpic_eoi(dev, irq);
734 }