Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2fd02375 authored by Daniel Wolstenholme's avatar Daniel Wolstenholme Committed by Russell King
Browse files

[ARM] iop13xx: msi support



Enable devices to signal interrupts via PCI memory cycles.

rev6:
* fix enable/disable typo, Michael Ellerman

rev5:
* fix up ack, enable, and disable for iop13xx_msi_chip

rev4:
* move smp compile fix to separate patch
* use dynamic_irq_init in create_irq()
* hookup mask/unmask routines in iop13xx_msi_chip

rev3:
* change msi.c to use linux/smp.h instead of asm/smp.h
* call dynamic_irq_cleanup at destroy_irq time

rev2:
* destroy_irq did not take the full 128 bits of msi_irq_in_use into account
* added missing '&' for calls to test_and_set_bit and clear_bit

[ebiederm@xmission.com: review comments/suggestions]
[dan.j.williams@intel.com: cleanups/forward port to 2.6-git]
Signed-off-by: default avatarDaniel Wolstenholme <daniel.e.wolstenholme@intel.com>
Signed-off-by: default avatarDan Williams <dan.j.williams@intel.com>
Acked-by: default avatarEric W. Biederman <ebiederm@xmission.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarRussell King <rmk+kernel@arm.linux.org.uk>
parent 87b247c4
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -10,3 +10,4 @@ obj-$(CONFIG_ARCH_IOP13XX) += io.o
obj-$(CONFIG_ARCH_IOP13XX) += tpmi.o
obj-$(CONFIG_MACH_IQ81340SC) += iq81340sc.o
obj-$(CONFIG_MACH_IQ81340MC) += iq81340mc.o
obj-$(CONFIG_PCI_MSI) += msi.o
+4 −1
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@
#include <asm/hardware.h>
#include <asm/mach-types.h>
#include <asm/arch/irqs.h>
#include <asm/arch/msi.h>

/* INTCTL0 CP6 R0 Page 4
 */
@@ -258,7 +259,7 @@ void __init iop13xx_init_irq(void)
	write_intbase(INTBASE);
	write_intsize(INTSIZE_4);

	for(i = 0; i < NR_IOP13XX_IRQS; i++) {
	for(i = 0; i <= IRQ_IOP13XX_HPI; i++) {
		if (i < 32)
			set_irq_chip(i, &iop13xx_irqchip1);
		else if (i < 64)
@@ -271,4 +272,6 @@ void __init iop13xx_init_irq(void)
		set_irq_handler(i, handle_level_irq);
		set_irq_flags(i, IRQF_VALID | IRQF_PROBE);
	}

	iop13xx_msi_init();
}
+194 −0
Original line number Diff line number Diff line
/*
 * arch/arm/mach-iop13xx/msi.c
 *
 * PCI MSI support for the iop13xx processor
 *
 * Copyright (c) 2006, Intel Corporation.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms and conditions of the GNU General Public License,
 * version 2, as published by the Free Software Foundation.
 *
 * This program is distributed in the hope it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
 * Place - Suite 330, Boston, MA 02111-1307 USA.
 *
 */
#include <linux/pci.h>
#include <linux/msi.h>
#include <asm/mach/irq.h>
#include <asm/irq.h>


#define IOP13XX_NUM_MSI_IRQS 128
static DECLARE_BITMAP(msi_irq_in_use, IOP13XX_NUM_MSI_IRQS);

/* IMIPR0 CP6 R8 Page 1
 */
static inline u32 read_imipr_0(void)
{
	u32 val;
	asm volatile("mrc p6, 0, %0, c8, c1, 0":"=r" (val));
	return val;
}
static inline void write_imipr_0(u32 val)
{
	asm volatile("mcr p6, 0, %0, c8, c1, 0"::"r" (val));
}

/* IMIPR1 CP6 R9 Page 1
 */
static inline u32 read_imipr_1(void)
{
	u32 val;
	asm volatile("mrc p6, 0, %0, c9, c1, 0":"=r" (val));
	return val;
}
static inline void write_imipr_1(u32 val)
{
	asm volatile("mcr p6, 0, %0, c9, c1, 0"::"r" (val));
}

/* IMIPR2 CP6 R10 Page 1
 */
static inline u32 read_imipr_2(void)
{
	u32 val;
	asm volatile("mrc p6, 0, %0, c10, c1, 0":"=r" (val));
	return val;
}
static inline void write_imipr_2(u32 val)
{
	asm volatile("mcr p6, 0, %0, c10, c1, 0"::"r" (val));
}

/* IMIPR3 CP6 R11 Page 1
 */
static inline u32 read_imipr_3(void)
{
	u32 val;
	asm volatile("mrc p6, 0, %0, c11, c1, 0":"=r" (val));
	return val;
}
static inline void write_imipr_3(u32 val)
{
	asm volatile("mcr p6, 0, %0, c11, c1, 0"::"r" (val));
}

static u32 (*read_imipr[])(void) = {
	read_imipr_0,
	read_imipr_1,
	read_imipr_2,
	read_imipr_3,
};

static void (*write_imipr[])(u32) = {
	write_imipr_0,
	write_imipr_1,
	write_imipr_2,
	write_imipr_3,
};

static void iop13xx_msi_handler(unsigned int irq, struct irq_desc *desc)
{
	int i, j;
	unsigned long status;

	/* read IMIPR registers and find any active interrupts,
	 * then call ISR for each active interrupt
	 */
	for (i = 0; i < ARRAY_SIZE(read_imipr); i++) {
		status = (read_imipr[i])();
		if (!status)
			continue;

		do {
			j = find_first_bit(&status, 32);
			(write_imipr[i])(1 << j); /* write back to clear bit */
			desc = irq_desc + IRQ_IOP13XX_MSI_0 + j + (32*i);
			desc_handle_irq(IRQ_IOP13XX_MSI_0 + j + (32*i),	desc);
			status = (read_imipr[i])();
		} while (status);
	}
}

void __init iop13xx_msi_init(void)
{
	set_irq_chained_handler(IRQ_IOP13XX_INBD_MSI, iop13xx_msi_handler);
}

/*
 * Dynamic irq allocate and deallocation
 */
int create_irq(void)
{
	int irq, pos;

again:
	pos = find_first_zero_bit(msi_irq_in_use, IOP13XX_NUM_MSI_IRQS);
	irq = IRQ_IOP13XX_MSI_0 + pos;
	if (irq > NR_IRQS)
		return -ENOSPC;
	/* test_and_set_bit operates on 32-bits at a time */
	if (test_and_set_bit(pos, msi_irq_in_use))
		goto again;

	dynamic_irq_init(irq);

	return irq;
}

void destroy_irq(unsigned int irq)
{
	int pos = irq - IRQ_IOP13XX_MSI_0;

	dynamic_irq_cleanup(irq);

	clear_bit(pos, msi_irq_in_use);
}

void arch_teardown_msi_irq(unsigned int irq)
{
	destroy_irq(irq);
}

static void iop13xx_msi_nop(unsigned int irq)
{
	return;
}

static struct irq_chip iop13xx_msi_chip = {
	.name = "PCI-MSI",
	.ack = iop13xx_msi_nop,
	.enable = unmask_msi_irq,
	.disable = mask_msi_irq,
	.mask = mask_msi_irq,
	.unmask = unmask_msi_irq,
};

int arch_setup_msi_irq(struct pci_dev *pdev, struct msi_desc *desc)
{
	int id, irq = create_irq();
	struct msi_msg msg;

	if (irq < 0)
		return irq;

	set_irq_msi(irq, desc);

	msg.address_hi = 0x0;
	msg.address_lo = IOP13XX_MU_MIMR_PCI;

	id = iop13xx_cpu_id();
	msg.data = (id << IOP13XX_MU_MIMR_CORE_SELECT) | (irq & 0x7f);

	write_msi_msg(irq, &msg);
	set_irq_chip_and_handler(irq, &iop13xx_msi_chip, handle_simple_irq);

	return irq;
}
+16 −0
Original line number Diff line number Diff line
@@ -559,6 +559,14 @@ void __init iop13xx_atue_setup(void)
	int func = iop13xx_atu_function(IOP13XX_INIT_ATU_ATUE);
	u32 reg_val;

#ifdef CONFIG_PCI_MSI
	/* BAR 0 (inbound msi window) */
	__raw_writel(IOP13XX_MU_BASE_PHYS, IOP13XX_MU_MUBAR);
	__raw_writel(~(IOP13XX_MU_WINDOW_SIZE - 1), IOP13XX_ATUE_IALR0);
	__raw_writel(IOP13XX_MU_BASE_PHYS, IOP13XX_ATUE_IATVR0);
	__raw_writel(IOP13XX_MU_BASE_PCI, IOP13XX_ATUE_IABAR0);
#endif

	/* BAR 1 (1:1 mapping with Physical RAM) */
	/* Set limit and enable */
	__raw_writel(~(IOP13XX_MAX_RAM_SIZE - PHYS_OFFSET - 1) & ~0x1,
@@ -720,6 +728,14 @@ void __init iop13xx_atux_setup(void)
	else
		atux_trhfa_timeout = jiffies;

#ifdef CONFIG_PCI_MSI
	/* BAR 0 (inbound msi window) */
	__raw_writel(IOP13XX_MU_BASE_PHYS, IOP13XX_MU_MUBAR);
	__raw_writel(~(IOP13XX_MU_WINDOW_SIZE - 1), IOP13XX_ATUX_IALR0);
	__raw_writel(IOP13XX_MU_BASE_PHYS, IOP13XX_ATUX_IATVR0);
	__raw_writel(IOP13XX_MU_BASE_PCI, IOP13XX_ATUX_IABAR0);
#endif

	/* BAR 1 (1:1 mapping with Physical RAM) */
	/* Set limit and enable */
	__raw_writel(~(IOP13XX_MAX_RAM_SIZE - PHYS_OFFSET - 1) & ~0x1,
+29 −0
Original line number Diff line number Diff line
@@ -181,6 +181,7 @@ static inline int iop13xx_cpu_id(void)
#define IOP13XX_ADMA1_PMMR_OFFSET  	0x00000200
#define IOP13XX_ADMA2_PMMR_OFFSET  	0x00000400
#define IOP13XX_PBI_PMMR_OFFSET    	0x00001580
#define IOP13XX_MU_PMMR_OFFSET		0x00004000
#define IOP13XX_ESSR0_PMMR_OFFSET  	0x00002188
#define IOP13XX_ESSR0			IOP13XX_REG_ADDR32(0x00002188)

@@ -412,6 +413,34 @@ static inline int iop13xx_cpu_id(void)
#define IOP13XX_ATU_OUMBAR_FUNC_NUM_MASK  	(0x7)
/*=======================================================================*/

/*============================MESSAGING UNIT=============================*/
#define IOP13XX_MU_OFFSET(ofs)	IOP13XX_REG_ADDR32(IOP13XX_MU_PMMR_OFFSET +\
							(ofs))

#define IOP13XX_MU_IMR0	IOP13XX_MU_OFFSET(0x10)
#define IOP13XX_MU_IMR1	IOP13XX_MU_OFFSET(0x14)
#define IOP13XX_MU_OMR0	IOP13XX_MU_OFFSET(0x18)
#define IOP13XX_MU_OMR1	IOP13XX_MU_OFFSET(0x1C)
#define IOP13XX_MU_IDR	       	IOP13XX_MU_OFFSET(0x20)
#define IOP13XX_MU_IISR	IOP13XX_MU_OFFSET(0x24)
#define IOP13XX_MU_IIMR	IOP13XX_MU_OFFSET(0x28)
#define IOP13XX_MU_ODR	       	IOP13XX_MU_OFFSET(0x2C)
#define IOP13XX_MU_OISR	IOP13XX_MU_OFFSET(0x30)
#define IOP13XX_MU_OIMR	IOP13XX_MU_OFFSET(0x34)
#define IOP13XX_MU_IRCSR      	IOP13XX_MU_OFFSET(0x38)
#define IOP13XX_MU_ORCSR      	IOP13XX_MU_OFFSET(0x3C)
#define IOP13XX_MU_MIMR	IOP13XX_MU_OFFSET(0x48)
#define IOP13XX_MU_MUCR	IOP13XX_MU_OFFSET(0x50)
#define IOP13XX_MU_QBAR	IOP13XX_MU_OFFSET(0x54)
#define IOP13XX_MU_MUBAR      	IOP13XX_MU_OFFSET(0x84)

#define IOP13XX_MU_WINDOW_SIZE	(8 * 1024)
#define IOP13XX_MU_BASE_PHYS	(0xff000000)
#define IOP13XX_MU_BASE_PCI	(0xff000000)
#define IOP13XX_MU_MIMR_PCI	(IOP13XX_MU_BASE_PCI + 0x48)
#define IOP13XX_MU_MIMR_CORE_SELECT (15)
/*=======================================================================*/

/*==============================ADMA UNITS===============================*/
#define IOP13XX_ADMA_PHYS_BASE(chan)	IOP13XX_REG_ADDR32_PHYS((chan << 9))
#define IOP13XX_ADMA_UPPER_PA(chan)	(IOP13XX_ADMA_PHYS_BASE(chan) + 0xc0)
Loading