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