atpic.c revision 122897
189857Sobrien/*-
289857Sobrien * Copyright (c) 2003 John Baldwin <jhb@FreeBSD.org>
389857Sobrien * All rights reserved.
489857Sobrien *
5218822Sdim * Redistribution and use in source and binary forms, with or without
6218822Sdim * modification, are permitted provided that the following conditions
7218822Sdim * are met:
889857Sobrien * 1. Redistributions of source code must retain the above copyright
989857Sobrien *    notice, this list of conditions and the following disclaimer.
1089857Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1189857Sobrien *    notice, this list of conditions and the following disclaimer in the
1289857Sobrien *    documentation and/or other materials provided with the distribution.
1389857Sobrien * 3. Neither the name of the author nor the names of any co-contributors
1489857Sobrien *    may be used to endorse or promote products derived from this software
1589857Sobrien *    without specific prior written permission.
1689857Sobrien *
1789857Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1889857Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1989857Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2089857Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2189857Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22218822Sdim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23218822Sdim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2489857Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2589857Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2689857Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2789857Sobrien * SUCH DAMAGE.
2889857Sobrien */
2989857Sobrien
30130561Sobrien/*
3189857Sobrien * PIC driver for the 8259A Master and Slave PICs in PC/AT machines.
3289857Sobrien */
3389857Sobrien
3489857Sobrien#include <sys/cdefs.h>
3589857Sobrien__FBSDID("$FreeBSD: head/sys/i386/isa/atpic.c 122897 2003-11-19 15:38:56Z jhb $");
3689857Sobrien
37218822Sdim#include "opt_auto_eoi.h"
38218822Sdim#include "opt_isa.h"
39218822Sdim
40218822Sdim#include <sys/param.h>
4189857Sobrien#include <sys/systm.h>
4289857Sobrien#include <sys/bus.h>
43130561Sobrien#include <sys/interrupt.h>
44130561Sobrien#include <sys/kernel.h>
4589857Sobrien#include <sys/lock.h>
4689857Sobrien#include <sys/mutex.h>
4789857Sobrien
4889857Sobrien#include <machine/cpufunc.h>
4989857Sobrien#include <machine/frame.h>
5089857Sobrien#include <machine/intr_machdep.h>
5189857Sobrien#include <machine/md_var.h>
5289857Sobrien#include <machine/resource.h>
5389857Sobrien#include <machine/segments.h>
5489857Sobrien
5589857Sobrien#include <i386/isa/icu.h>
5689857Sobrien#ifdef PC98
5789857Sobrien#include <pc98/pc98/pc98.h>
5889857Sobrien#else
5989857Sobrien#include <i386/isa/isa.h>
6089857Sobrien#endif
6189857Sobrien#include <isa/isavar.h>
6289857Sobrien
6389857Sobrien#define	MASTER	0
6489857Sobrien#define	SLAVE	1
6589857Sobrien
6689857Sobrien/* XXX: Magic numbers */
6789857Sobrien#ifdef PC98
6889857Sobrien#ifdef AUTO_EOI_1
6989857Sobrien#define	MASTER_MODE	0x1f	/* Master auto EOI, 8086 mode */
7089857Sobrien#else
7189857Sobrien#define	MASTER_MODE	0x1d	/* Master 8086 mode */
7289857Sobrien#endif
7389857Sobrien#define	SLAVE_MODE	9	/* 8086 mode */
7489857Sobrien#else /* IBM-PC */
7589857Sobrien#ifdef AUTO_EOI_1
7689857Sobrien#define	MASTER_MODE	(ICW4_8086 | ICW4_AEOI)
7789857Sobrien#else
7889857Sobrien#define	MASTER_MODE	ICW4_8086
7989857Sobrien#endif
8089857Sobrien#ifdef AUTO_EOI_2
8189857Sobrien#define	SLAVE_MODE	(ICW4_8086 | ICW4_AEOI)
8289857Sobrien#else
8389857Sobrien#define	SLAVE_MODE	ICW4_8086
8489857Sobrien#endif
8589857Sobrien#endif /* PC98 */
8689857Sobrien
8789857Sobrienstatic void	atpic_init(void *dummy);
8889857Sobrien
8989857Sobrienunsigned int imen;	/* XXX */
9089857Sobrien
9189857Sobrieninthand_t
9289857Sobrien	IDTVEC(atpic_intr0), IDTVEC(atpic_intr1), IDTVEC(atpic_intr2),
9389857Sobrien	IDTVEC(atpic_intr3), IDTVEC(atpic_intr4), IDTVEC(atpic_intr5),
9489857Sobrien	IDTVEC(atpic_intr6), IDTVEC(atpic_intr7), IDTVEC(atpic_intr8),
9589857Sobrien	IDTVEC(atpic_intr9), IDTVEC(atpic_intr10), IDTVEC(atpic_intr11),
9689857Sobrien	IDTVEC(atpic_intr12), IDTVEC(atpic_intr13), IDTVEC(atpic_intr14),
9789857Sobrien	IDTVEC(atpic_intr15);
9889857Sobrien
9989857Sobrien#define	IRQ(ap, ai)	((ap)->at_irqbase + (ai)->at_irq)
10089857Sobrien
10189857Sobrien#define	ATPIC(io, base, eoi, imenptr)				\
10289857Sobrien     	{ { atpic_enable_source, atpic_disable_source, (eoi),		\
10389857Sobrien	    atpic_enable_intr, atpic_vector, atpic_source_pending, NULL, \
10489857Sobrien	    atpic_resume }, (io), (base), IDT_IO_INTS + (base), (imenptr) }
10589857Sobrien
10689857Sobrien#define	INTSRC(irq)							\
10789857Sobrien	{ { &atpics[(irq) / 8].at_pic }, (irq) % 8,			\
10889857Sobrien	    IDTVEC(atpic_intr ## irq ) }
10989857Sobrien
11089857Sobrienstruct atpic {
11189857Sobrien	struct pic at_pic;
11289857Sobrien	int	at_ioaddr;
11389857Sobrien	int	at_irqbase;
11489857Sobrien	uint8_t	at_intbase;
11589857Sobrien	uint8_t	*at_imen;
11689857Sobrien};
11789857Sobrien
11889857Sobrienstruct atpic_intsrc {
11989857Sobrien	struct intsrc at_intsrc;
12089857Sobrien	int	at_irq;		/* Relative to PIC base. */
12189857Sobrien	inthand_t *at_intr;
12289857Sobrien	u_long	at_count;
12389857Sobrien	u_long	at_straycount;
12489857Sobrien};
12589857Sobrien
12689857Sobrienstatic void atpic_enable_source(struct intsrc *isrc);
12789857Sobrienstatic void atpic_disable_source(struct intsrc *isrc);
12889857Sobrienstatic void atpic_eoi_master(struct intsrc *isrc);
12989857Sobrienstatic void atpic_eoi_slave(struct intsrc *isrc);
13089857Sobrienstatic void atpic_enable_intr(struct intsrc *isrc);
13189857Sobrienstatic int atpic_vector(struct intsrc *isrc);
13289857Sobrienstatic void atpic_resume(struct intsrc *isrc);
13389857Sobrienstatic int atpic_source_pending(struct intsrc *isrc);
13489857Sobrienstatic void i8259_init(struct atpic *pic, int slave);
13589857Sobrien
13689857Sobrienstatic struct atpic atpics[] = {
13789857Sobrien	ATPIC(IO_ICU1, 0, atpic_eoi_master, (uint8_t *)&imen),
13889857Sobrien	ATPIC(IO_ICU2, 8, atpic_eoi_slave, ((uint8_t *)&imen) + 1)
13989857Sobrien};
14089857Sobrien
14189857Sobrienstatic struct atpic_intsrc atintrs[] = {
14289857Sobrien	INTSRC(0),
14389857Sobrien	INTSRC(1),
14489857Sobrien	INTSRC(2),
14589857Sobrien	INTSRC(3),
14689857Sobrien	INTSRC(4),
14789857Sobrien	INTSRC(5),
14889857Sobrien	INTSRC(6),
14989857Sobrien	INTSRC(7),
15089857Sobrien	INTSRC(8),
15189857Sobrien	INTSRC(9),
15289857Sobrien	INTSRC(10),
15389857Sobrien	INTSRC(11),
15489857Sobrien	INTSRC(12),
15589857Sobrien	INTSRC(13),
15689857Sobrien	INTSRC(14),
15789857Sobrien	INTSRC(15),
15889857Sobrien};
15989857Sobrien
16089857Sobrienstatic void
16189857Sobrienatpic_enable_source(struct intsrc *isrc)
16289857Sobrien{
16389857Sobrien	struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc;
16489857Sobrien	struct atpic *ap = (struct atpic *)isrc->is_pic;
16589857Sobrien
16689857Sobrien	mtx_lock_spin(&icu_lock);
16789857Sobrien	*ap->at_imen &= ~(1 << ai->at_irq);
16889857Sobrien	outb(ap->at_ioaddr + ICU_IMR_OFFSET, *ap->at_imen);
16989857Sobrien	mtx_unlock_spin(&icu_lock);
17089857Sobrien}
17189857Sobrien
17289857Sobrienstatic void
17389857Sobrienatpic_disable_source(struct intsrc *isrc)
17489857Sobrien{
17589857Sobrien	struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc;
17689857Sobrien	struct atpic *ap = (struct atpic *)isrc->is_pic;
17789857Sobrien
17889857Sobrien	mtx_lock_spin(&icu_lock);
17989857Sobrien	*ap->at_imen |= (1 << ai->at_irq);
18089857Sobrien	outb(ap->at_ioaddr + ICU_IMR_OFFSET, *ap->at_imen);
18189857Sobrien	mtx_unlock_spin(&icu_lock);
18289857Sobrien}
18389857Sobrien
18489857Sobrienstatic void
18589857Sobrienatpic_eoi_master(struct intsrc *isrc)
18689857Sobrien{
18789857Sobrien
18889857Sobrien	KASSERT(isrc->is_pic == &atpics[MASTER].at_pic,
18989857Sobrien	    ("%s: mismatched pic", __func__));
19089857Sobrien#ifndef AUTO_EOI_1
19189857Sobrien	mtx_lock_spin(&icu_lock);
19289857Sobrien	outb(atpics[MASTER].at_ioaddr, ICU_EOI);
19389857Sobrien	mtx_unlock_spin(&icu_lock);
19489857Sobrien#endif
19589857Sobrien}
19689857Sobrien
19789857Sobrien/*
19889857Sobrien * The data sheet says no auto-EOI on slave, but it sometimes works.
19989857Sobrien * So, if AUTO_EOI_2 is enabled, we use it.
20089857Sobrien */
20189857Sobrienstatic void
20289857Sobrienatpic_eoi_slave(struct intsrc *isrc)
20389857Sobrien{
20489857Sobrien
20589857Sobrien	KASSERT(isrc->is_pic == &atpics[SLAVE].at_pic,
20689857Sobrien	    ("%s: mismatched pic", __func__));
20789857Sobrien#ifndef AUTO_EOI_2
20889857Sobrien	mtx_lock_spin(&icu_lock);
20989857Sobrien	outb(atpics[SLAVE].at_ioaddr, ICU_EOI);
21089857Sobrien#ifndef AUTO_EOI_1
21189857Sobrien	outb(atpics[MASTER].at_ioaddr, ICU_EOI);
21289857Sobrien#endif
21389857Sobrien	mtx_unlock_spin(&icu_lock);
21489857Sobrien#endif
21589857Sobrien}
21689857Sobrien
21789857Sobrienstatic void
21889857Sobrienatpic_enable_intr(struct intsrc *isrc)
21989857Sobrien{
22089857Sobrien}
22189857Sobrien
22289857Sobrienstatic int
22389857Sobrienatpic_vector(struct intsrc *isrc)
22489857Sobrien{
22589857Sobrien	struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc;
22689857Sobrien	struct atpic *ap = (struct atpic *)isrc->is_pic;
22789857Sobrien
22889857Sobrien	return (IRQ(ap, ai));
22989857Sobrien}
23089857Sobrien
23189857Sobrienstatic int
23289857Sobrienatpic_source_pending(struct intsrc *isrc)
23389857Sobrien{
23489857Sobrien	struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc;
23589857Sobrien	struct atpic *ap = (struct atpic *)isrc->is_pic;
23689857Sobrien
23789857Sobrien	return (inb(ap->at_ioaddr) & (1 << ai->at_irq));
23889857Sobrien}
23989857Sobrien
24089857Sobrienstatic void
24189857Sobrienatpic_resume(struct intsrc *isrc)
24289857Sobrien{
24389857Sobrien	struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc;
24489857Sobrien	struct atpic *ap = (struct atpic *)isrc->is_pic;
24589857Sobrien
24689857Sobrien	if (ai->at_irq == 0)
24789857Sobrien		i8259_init(ap, ap == &atpics[SLAVE]);
24889857Sobrien}
24989857Sobrien
25089857Sobrienstatic void
25189857Sobrieni8259_init(struct atpic *pic, int slave)
25289857Sobrien{
25389857Sobrien	int imr_addr;
25489857Sobrien
25589857Sobrien	/* Reset the PIC and program with next four bytes. */
25689857Sobrien	mtx_lock_spin(&icu_lock);
25789857Sobrien#ifdef DEV_MCA
25889857Sobrien	/* MCA uses level triggered interrupts. */
25989857Sobrien	if (MCA_system)
26089857Sobrien		outb(pic->at_ioaddr, ICW1_RESET | ICW1_IC4 | ICW1_LTIM);
26189857Sobrien	else
26289857Sobrien#endif
26389857Sobrien		outb(pic->at_ioaddr, ICW1_RESET | ICW1_IC4);
26489857Sobrien	imr_addr = pic->at_ioaddr + ICU_IMR_OFFSET;
26589857Sobrien
26689857Sobrien	/* Start vector. */
26789857Sobrien	outb(imr_addr, pic->at_intbase);
26889857Sobrien
26989857Sobrien	/*
27089857Sobrien	 * Setup slave links.  For the master pic, indicate what line
27189857Sobrien	 * the slave is configured on.  For the slave indicate
27289857Sobrien	 * which line on the master we are connected to.
27389857Sobrien	 */
274218822Sdim	if (slave)
27589857Sobrien		outb(imr_addr, ICU_SLAVEID);	/* my slave id is 7 */
27689857Sobrien	else
277218822Sdim		outb(imr_addr, IRQ_SLAVE);	/* slave on line 7 */
27889857Sobrien
27989857Sobrien	/* Set mode. */
28089857Sobrien	if (slave)
28189857Sobrien		outb(imr_addr, SLAVE_MODE);
28289857Sobrien	else
28389857Sobrien		outb(imr_addr, MASTER_MODE);
28489857Sobrien
28589857Sobrien	/* Set interrupt enable mask. */
28689857Sobrien	outb(imr_addr, *pic->at_imen);
28789857Sobrien
28889857Sobrien	/* Reset is finished, default to IRR on read. */
28989857Sobrien	outb(pic->at_ioaddr, OCW3_SEL | OCW3_RR);
29089857Sobrien
29189857Sobrien#ifndef PC98
29289857Sobrien	/* OCW2_L1 sets priority order to 3-7, 0-2 (com2 first). */
29389857Sobrien	if (!slave)
29489857Sobrien		outb(pic->at_ioaddr, OCW2_R | OCW2_SL | OCW2_L1);
29589857Sobrien#endif
29689857Sobrien	mtx_unlock_spin(&icu_lock);
29789857Sobrien}
29889857Sobrien
29989857Sobrienvoid
30089857Sobrienatpic_startup(void)
30189857Sobrien{
30289857Sobrien	struct atpic_intsrc *ai;
30389857Sobrien	int i;
30489857Sobrien
30589857Sobrien	/* Start off with all interrupts disabled. */
30689857Sobrien	imen = 0xffff;
30789857Sobrien	i8259_init(&atpics[MASTER], 0);
30889857Sobrien	i8259_init(&atpics[SLAVE], 1);
30989857Sobrien	atpic_enable_source((struct intsrc *)&atintrs[ICU_SLAVEID]);
31089857Sobrien
31189857Sobrien	/* Install low-level interrupt handlers for all of our IRQs. */
31289857Sobrien	for (i = 0; i < sizeof(atintrs) / sizeof(struct atpic_intsrc); i++) {
31389857Sobrien		if (i == ICU_SLAVEID)
31489857Sobrien			continue;
31589857Sobrien		ai = &atintrs[i];
31689857Sobrien		ai->at_intsrc.is_count = &ai->at_count;
31789857Sobrien		ai->at_intsrc.is_straycount = &ai->at_straycount;
31889857Sobrien		setidt(((struct atpic *)ai->at_intsrc.is_pic)->at_intbase +
31989857Sobrien		    ai->at_irq, ai->at_intr, SDT_SYS386IGT, SEL_KPL,
32089857Sobrien		    GSEL(GCODE_SEL, SEL_KPL));
32189857Sobrien	}
32289857Sobrien}
32389857Sobrien
32489857Sobrienstatic void
32589857Sobrienatpic_init(void *dummy __unused)
32689857Sobrien{
32789857Sobrien	int i;
32889857Sobrien
32989857Sobrien	/* Loop through all interrupt sources and add them. */
33089857Sobrien	for (i = 0; i < sizeof(atintrs) / sizeof(struct atpic_intsrc); i++) {
33189857Sobrien		if (i == ICU_SLAVEID)
33289857Sobrien			continue;
33389857Sobrien		intr_register_source(&atintrs[i].at_intsrc);
33489857Sobrien	}
33589857Sobrien}
33689857SobrienSYSINIT(atpic_init, SI_SUB_INTR, SI_ORDER_SECOND + 1, atpic_init, NULL)
33789857Sobrien
33889857Sobrienvoid
33989857Sobrienatpic_handle_intr(struct intrframe iframe)
34089857Sobrien{
34189857Sobrien	struct intsrc *isrc;
34289857Sobrien
34389857Sobrien	KASSERT((uint)iframe.if_vec < ICU_LEN,
34489857Sobrien	    ("unknown int %d\n", iframe.if_vec));
34589857Sobrien	isrc = &atintrs[iframe.if_vec].at_intsrc;
34689857Sobrien	intr_execute_handlers(isrc, &iframe);
34789857Sobrien}
34889857Sobrien
34989857Sobrien#ifdef DEV_ISA
35089857Sobrien/*
35189857Sobrien * Bus attachment for the ISA PIC.
35289857Sobrien */
35389857Sobrienstatic struct isa_pnp_id atpic_ids[] = {
35489857Sobrien	{ 0x0000d041 /* PNP0000 */, "AT interrupt controller" },
35589857Sobrien	{ 0 }
35689857Sobrien};
35789857Sobrien
35889857Sobrienstatic int
35989857Sobrienatpic_probe(device_t dev)
36089857Sobrien{
36189857Sobrien	int result;
36289857Sobrien
36389857Sobrien	result = ISA_PNP_PROBE(device_get_parent(dev), dev, atpic_ids);
36489857Sobrien	if (result <= 0)
36589857Sobrien		device_quiet(dev);
36689857Sobrien	return (result);
36789857Sobrien}
36889857Sobrien
36989857Sobrien/*
37089857Sobrien * We might be granted IRQ 2, as this is typically consumed by chaining
37189857Sobrien * between the two PIC components.  If we're using the APIC, however,
37289857Sobrien * this may not be the case, and as such we should free the resource.
37389857Sobrien * (XXX untested)
37489857Sobrien *
37589857Sobrien * The generic ISA attachment code will handle allocating any other resources
37689857Sobrien * that we don't explicitly claim here.
37789857Sobrien */
37889857Sobrienstatic int
37989857Sobrienatpic_attach(device_t dev)
38089857Sobrien{
38189857Sobrien	struct resource *res;
38289857Sobrien	int rid;
38389857Sobrien
38489857Sobrien	/* Try to allocate our IRQ and then free it. */
38589857Sobrien	rid = 0;
38689857Sobrien	res = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, 0);
38789857Sobrien	if (res != NULL)
38889857Sobrien		bus_release_resource(dev, SYS_RES_IRQ, rid, res);
38989857Sobrien	return (0);
39089857Sobrien}
39189857Sobrien
39289857Sobrienstatic device_method_t atpic_methods[] = {
39389857Sobrien	/* Device interface */
39489857Sobrien	DEVMETHOD(device_probe,		atpic_probe),
39589857Sobrien	DEVMETHOD(device_attach,	atpic_attach),
39689857Sobrien	DEVMETHOD(device_detach,	bus_generic_detach),
39789857Sobrien	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
39889857Sobrien	DEVMETHOD(device_suspend,	bus_generic_suspend),
39989857Sobrien	DEVMETHOD(device_resume,	bus_generic_resume),
40089857Sobrien	{ 0, 0 }
40189857Sobrien};
40289857Sobrien
40389857Sobrienstatic driver_t atpic_driver = {
40489857Sobrien	"atpic",
40589857Sobrien	atpic_methods,
40689857Sobrien	1,		/* no softc */
40789857Sobrien};
408218822Sdim
409218822Sdimstatic devclass_t atpic_devclass;
410218822Sdim
41189857SobrienDRIVER_MODULE(atpic, isa, atpic_driver, atpic_devclass, 0, 0);
412218822Sdim#ifndef PC98
413218822SdimDRIVER_MODULE(atpic, acpi, atpic_driver, atpic_devclass, 0, 0);
414218822Sdim#endif
415218822Sdim
416218822Sdim/*
417218822Sdim * Return a bitmap of the current interrupt requests.  This is 8259-specific
418218822Sdim * and is only suitable for use at probe time.
419218822Sdim */
420218822Sdimintrmask_t
421218822Sdimisa_irq_pending(void)
422218822Sdim{
423218822Sdim	u_char irr1;
42489857Sobrien	u_char irr2;
42589857Sobrien
426218822Sdim	irr1 = inb(IO_ICU1);
427218822Sdim	irr2 = inb(IO_ICU2);
428218822Sdim	return ((irr2 << 8) | irr1);
42989857Sobrien}
43089857Sobrien#endif /* DEV_ISA */
431218822Sdim