From patchwork Wed Nov 12 00:18:04 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 409737 X-Patchwork-Delegate: sjg@chromium.org Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from theia.denx.de (theia.denx.de [85.214.87.163]) by ozlabs.org (Postfix) with ESMTP id 3AFB91400F4 for ; Wed, 12 Nov 2014 11:21:15 +1100 (AEDT) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 6B47E4BA9D; Wed, 12 Nov 2014 01:20:38 +0100 (CET) Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id IQ+-+L6Ym9xt; Wed, 12 Nov 2014 01:20:38 +0100 (CET) Received: from theia.denx.de (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id A71224BA46; Wed, 12 Nov 2014 01:19:27 +0100 (CET) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 5E64F4B97C for ; Wed, 12 Nov 2014 01:18:47 +0100 (CET) Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id hoZQMd-xm5fP for ; Wed, 12 Nov 2014 01:18:47 +0100 (CET) X-policyd-weight: NOT_IN_SBL_XBL_SPAMHAUS=-1.5 NOT_IN_SPAMCOP=-1.5 NOT_IN_BL_NJABL=-1.5 (only DNSBL check requested) Received: from mail-ig0-f202.google.com (mail-ig0-f202.google.com [209.85.213.202]) by theia.denx.de (Postfix) with ESMTPS id 09F8A4B999 for ; Wed, 12 Nov 2014 01:18:40 +0100 (CET) Received: by mail-ig0-f202.google.com with SMTP id r10so330714igi.5 for ; Tue, 11 Nov 2014 16:18:39 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=1HDQRDVyNlgEdIJm9i/rtpJW4Ysq7D3h0xDYq9KpMAk=; b=lTLTBDf+WOFBCjGsgUNEMOUehm++QC4jUxp5ZE1zVDAHTLBmc/mL5JGEeLN7dC7jSD /gC0cvCo9btEk3KJSBkM/XIVbNkRbFaTcKHjS9UhI9T9JW2y70svwJZpk2j6Wagg7Bqt j0yQ+B61LEto5gJ0LUJZ/F8RAZahj8rtrL7RBB/TehIixLsxU7UKqjjVMvawnIN2r4KI +Va79Q7POOr/VuErMs79078JRlszG85J6QIrXN63CsQEJD8cC+EdM9VlhRE6JONNHH8w pI3r4gXg4ovXR/2Ty5lnWTLnYtWzL8u3v4Qb4imjba7JwC+5LbSA7ejg8sWzLRC/A7Ow u6Aw== X-Gm-Message-State: ALoCoQnrT0Mkxt0j28UR9HlT9uqYAThttS0Rs4EvTfkFhfRRM9caIP0l0DhB+ajTKS+W/Nd+3eQh X-Received: by 10.182.111.164 with SMTP id ij4mr33500847obb.26.1415751519291; Tue, 11 Nov 2014 16:18:39 -0800 (PST) Received: from corpmail-nozzle1-1.hot.corp.google.com ([100.108.1.104]) by gmr-mx.google.com with ESMTPS id 5si861299yhd.6.2014.11.11.16.18.38 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Tue, 11 Nov 2014 16:18:39 -0800 (PST) Received: from kaki.bld.corp.google.com ([172.29.216.32]) by corpmail-nozzle1-1.hot.corp.google.com with ESMTP id GLo3pU6J.2; Tue, 11 Nov 2014 16:18:39 -0800 Received: by kaki.bld.corp.google.com (Postfix, from userid 121222) id 475B5221036; Tue, 11 Nov 2014 17:18:38 -0700 (MST) From: Simon Glass To: U-Boot Mailing List Date: Tue, 11 Nov 2014 17:18:04 -0700 Message-Id: <1415751501-23407-17-git-send-email-sjg@chromium.org> X-Mailer: git-send-email 2.1.0.rc2.206.gedb03e5 In-Reply-To: <1415751501-23407-1-git-send-email-sjg@chromium.org> References: <1415751501-23407-1-git-send-email-sjg@chromium.org> Cc: Graeme Russ Subject: [U-Boot] [PATCH 16/33] x86: Add basic i8259 implementation X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.13 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: u-boot-bounces@lists.denx.de Errors-To: u-boot-bounces@lists.denx.de The i8259 is a basic interrupt controller from the 1970s which is still present in modern Intel hardware. Add some code to set it up. Signed-off-by: Simon Glass --- arch/x86/include/asm/i8259.h | 3 + arch/x86/lib/Makefile | 1 + arch/x86/lib/i8259.c | 134 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 138 insertions(+) create mode 100644 arch/x86/lib/i8259.c diff --git a/arch/x86/include/asm/i8259.h b/arch/x86/include/asm/i8259.h index 73113f9..bac073d 100644 --- a/arch/x86/include/asm/i8259.h +++ b/arch/x86/include/asm/i8259.h @@ -69,4 +69,7 @@ #define ICW4_AEOI 0x02 /* Automatic EOI Mode */ #define ICW4_PM 0x01 /* Microprocessor Mode */ +void i8259_setup(void); +void i8259_configure_irq_trigger(int int_num, bool is_level_triggered); + #endif diff --git a/arch/x86/lib/Makefile b/arch/x86/lib/Makefile index d0c7f30..2d75f9f 100644 --- a/arch/x86/lib/Makefile +++ b/arch/x86/lib/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_CMD_BOOTM) += bootm.o obj-y += cmd_boot.o obj-y += gcc.o +obj-y += i8259.o obj-y += init_helpers.o obj-y += interrupts.o obj-$(CONFIG_SYS_PCAT_INTERRUPTS) += pcat_interrupts.o diff --git a/arch/x86/lib/i8259.c b/arch/x86/lib/i8259.c new file mode 100644 index 0000000..442d904 --- /dev/null +++ b/arch/x86/lib/i8259.c @@ -0,0 +1,134 @@ +/* + * From Coreboot file of the same name + * + * Copyright (C) 2009 coresystems GmbH + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include +#include +#include + +#define MASTER_PIC_ICW1 0x20 +#define SLAVE_PIC_ICW1 0xa0 +#define ICW_SELECT (1 << 4) +#define OCW_SELECT (0 << 4) +#define ADI (1 << 2) +#define SNGL (1 << 1) +#define IC4 (1 << 0) + +#define MASTER_PIC_ICW2 0x21 +#define SLAVE_PIC_ICW2 0xa1 +#define INT_VECTOR_MASTER 0x20 +#define IRQ0 0x00 +#define IRQ1 0x01 +#define INT_VECTOR_SLAVE 0x28 +#define IRQ8 0x00 +#define IRQ9 0x01 + +#define MASTER_PIC_ICW3 0x21 +#define CASCADED_PIC (1 << 2) + +#define MASTER_PIC_ICW4 0x21 +#define SLAVE_PIC_ICW4 0xa1 +#define MICROPROCESSOR_MODE (1 << 0) + +#define SLAVE_PIC_ICW3 0xa1 +#define SLAVE_ID 0x02 + +#define MASTER_PIC_OCW1 0x21 +#define SLAVE_PIC_OCW1 0xa1 +#define IRQ2 (1 << 2) +#define ALL_IRQS 0xff + +#define ELCR1 0x4d0 +#define ELCR2 0x4d1 + +void i8259_setup(void) +{ + /* A write to ICW1 starts the Interrupt Controller Initialization + * Sequence. This implicitly causes the following to happen: + * - Interrupt Mask register is cleared + * - Priority 7 is assigned to IRQ7 input + * - Slave mode address is set to 7 + * - Special mask mode is cleared + * + * We send the initialization sequence to both the master and + * slave i8259 controller. + */ + outb(ICW_SELECT|IC4, MASTER_PIC_ICW1); + outb(ICW_SELECT|IC4, SLAVE_PIC_ICW1); + + /* Now the interrupt controller expects us to write to ICW2. */ + outb(INT_VECTOR_MASTER | IRQ0, MASTER_PIC_ICW2); + outb(INT_VECTOR_SLAVE | IRQ8, SLAVE_PIC_ICW2); + + /* Now the interrupt controller expects us to write to ICW3. + * + * The normal scenario is to set up cascading on IRQ2 on the master + * i8259 and assign the slave ID 2 to the slave i8259. + */ + outb(CASCADED_PIC, MASTER_PIC_ICW3); + outb(SLAVE_ID, SLAVE_PIC_ICW3); + + /* Now the interrupt controller expects us to write to ICW4. + * + * We switch both i8259 to microprocessor mode because they're + * operating as part of an x86 architecture based chipset + */ + outb(MICROPROCESSOR_MODE, MASTER_PIC_ICW2); + outb(MICROPROCESSOR_MODE, SLAVE_PIC_ICW2); + + /* Now clear the interrupts through OCW1. + * First we mask off all interrupts on the slave interrupt controller + * then we mask off all interrupts but interrupt 2 on the master + * controller. This way the cascading stays alife. + */ + outb(ALL_IRQS, SLAVE_PIC_OCW1); + outb(ALL_IRQS & ~IRQ2, MASTER_PIC_OCW1); + debug("i8259 inited\n"); +} + +/** + * i8259_configure_irq_trigger() - Configure IRQ triggering + * + * Switch the given interrupt to be level / edge triggered + * + * @param int_num legacy interrupt number (3-7, 9-15) + * @param is_level_triggered true for level triggered interrupt, false for + * edge triggered interrupt + */ +void i8259_configure_irq_trigger(int int_num, bool is_level_triggered) +{ + u16 int_bits = inb(ELCR1) | (((u16)inb(ELCR2)) << 8); + + debug("%s: current interrupts are 0x%x\n", __func__, int_bits); + if (is_level_triggered) + int_bits |= (1 << int_num); + else + int_bits &= ~(1 << int_num); + + /* Write new values */ + debug("%s: try to set interrupts 0x%x\n", __func__, int_bits); + outb((u8)(int_bits & 0xff), ELCR1); + outb((u8)(int_bits >> 8), ELCR2); + +#ifdef PARANOID_IRQ_TRIGGERS + /* + * Try reading back the new values. This seems like an error but is + * not + */ + if (inb(ELCR1) != (int_bits & 0xff)) { + printf("%s: lower order bits are wrong: want 0x%x, got 0x%x\n", + __func__, (int_bits & 0xff), inb(ELCR1)); + } + + if (inb(ELCR2) != (int_bits >> 8)) { + printf("%s: higher order bits are wrong: want 0x%x, got 0x%x\n", + __func__, (int_bits>>8), inb(ELCR2)); + } +#endif +} + +