All of lore.kernel.org
 help / color / mirror / Atom feed
From: Benjamin Herrenschmidt <benh@kernel.crashing.org>
To: Michael Ellerman <michael@ellerman.id.au>
Cc: linuxppc-dev@ozlabs.org
Subject: Re: [RFC/PATCH 4/4] Add support for MSI on Axon-based Cell systems
Date: Tue, 05 Jun 2007 08:45:15 +1000	[thread overview]
Message-ID: <1180997115.31677.85.camel@localhost.localdomain> (raw)
In-Reply-To: <90f8b99fa6a8e4b8898d64630d96036f68668b03.1180961962.git.michael@ellerman.id.au>

On Mon, 2007-06-04 at 23:00 +1000, Michael Ellerman wrote:
> This patch adds support for the setup and decoding of MSIs
> on Axon-based Cell systems.
> 
> Signed-off-by: Michael Ellerman <michael@ellerman.id.au>

Acked-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>

> ---
> 
> This still needs a bit of cleanup, but sending now for an early review.
> 
>  arch/powerpc/platforms/cell/Makefile   |    2 +
>  arch/powerpc/platforms/cell/axon_msi.c |  412 ++++++++++++++++++++++++++++++++
>  2 files changed, 414 insertions(+), 0 deletions(-)
> 
> diff --git a/arch/powerpc/platforms/cell/Makefile b/arch/powerpc/platforms/cell/Makefile
> index 869af89..6615d40 100644
> --- a/arch/powerpc/platforms/cell/Makefile
> +++ b/arch/powerpc/platforms/cell/Makefile
> @@ -23,3 +23,5 @@ obj-$(CONFIG_SPU_BASE)			+= spu_callbacks.o spu_base.o \
>  					   $(spu-priv1-y) \
>  					   $(spu-manage-y) \
>  					   spufs/
> +
> +obj-$(CONFIG_PCI_MSI)			+= axon_msi.o
> diff --git a/arch/powerpc/platforms/cell/axon_msi.c b/arch/powerpc/platforms/cell/axon_msi.c
> new file mode 100644
> index 0000000..2709853
> --- /dev/null
> +++ b/arch/powerpc/platforms/cell/axon_msi.c
> @@ -0,0 +1,412 @@
> +/*
> + * Copyright 2007, Michael Ellerman, IBM Corporation.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation; either version
> + * 2 of the License, or (at your option) any later version.
> + */
> +
> +#include <linux/interrupt.h>
> +#include <linux/irq.h>
> +#include <linux/kernel.h>
> +#include <linux/pci.h>
> +#include <linux/msi.h>
> +#include <linux/reboot.h>
> +
> +#include <asm/dcr.h>
> +#include <asm/machdep.h>
> +#include <asm/prom.h>
> +
> +
> +/*
> + * MSIC Control Register
> + */
> +#define MSIC_CTRL_REG_ADDR	0x6F
> +
> +/* Flags */
> +#define MSIC_ENABLE		0x0001	/* Bit 31 */
> +#define MSIC_FIFO_FULL_ENABLE	0x0002	/* Bit 30 */
> +#define MSIC_IRQ_ENABLE		0x0008	/* Bit 28 */
> +#define MSIC_FULL_STOP_ENABLE	0x0010	/* Bit 27 */
> +
> +/* Size configuration constants */
> +#define MSIC_SIZE_MASK		0x0180	/* Bits 22:23 */
> +#define MSIC_SIZE_SHIFT		(7)
> +#define	MSIC_SIZE_32K		0x0
> +#define	MSIC_SIZE_64K		0x1
> +#define	MSIC_SIZE_128K		0x2
> +#define	MSIC_SIZE_256K		0x3
> +
> +/* The size we're actually using */
> +#define MSIC_FIFO_SIZE		MSIC_SIZE_32K
> +
> +/* Different representations of the fifo size */
> +#define MSIC_FIFO_SHIFT		(MSIC_FIFO_SIZE + 0xF)
> +#define MSIC_FIFO_BYTES		(1 << MSIC_FIFO_SHIFT)
> +#define MSIC_FIFO_MASK		(MSIC_FIFO_BYTES - 1)
> +#define MSIC_FIFO_ORDER		max(MSIC_FIFO_SHIFT - PAGE_SHIFT, 0)
> +
> +/*
> + * MSIC Base Address registers (FIFO location)
> + */
> +#define MSIC_BASE_ADDR_HI_REG	0x72
> +#define MSIC_BASE_ADDR_LO_REG	0x73
> +
> +#define MSIC_READ_OFFSET_REG	0x74
> +#define MSIC_WRITE_OFFSET_REG	0x75
> +
> +#define MSIC_DCR_BASE		MSIC_CTRL_REG_ADDR
> +#define MSIC_DCR_SIZE		(MSIC_WRITE_OFFSET_REG - MSIC_CTRL_REG_ADDR)
> +
> +
> +struct axon_msic {
> +	struct device_node *dn;
> +	struct irq_host *irq_host;
> +	void *fifo;
> +	dcr_host_t dcr_host;
> +	struct list_head list;
> +	u32 read_offset;
> +};
> +
> +
> +static LIST_HEAD(axon_msic_list);
> +
> +static void axon_msi_cascade(unsigned int irq, struct irq_desc *desc)
> +{
> +	struct axon_msic *msic = get_irq_data(irq);
> +	u32 write_offset, msi;
> +	unsigned int cirq;
> +
> +	write_offset = dcr_read(msic->dcr_host, MSIC_WRITE_OFFSET_REG);
> +	pr_debug("axon_msi: original write_offset 0x%x\n", write_offset);
> +
> +	/* write_offset doesn't wrap properly, so we have to mask it */
> +	write_offset &= MSIC_FIFO_MASK;
> +
> +	while (msic->read_offset != write_offset) {
> +		msi  = le32_to_cpup((__le32*)(msic->fifo + msic->read_offset));
> +		msi &= 0xFFFF;
> +
> +		pr_debug("axon_msi: woff %x roff %x @ %p msi %x msi@0 %x\n",
> +			write_offset, msic->read_offset,
> +			msic->fifo + msic->read_offset, msi, *(u32*)msic->fifo);
> +
> +		msic->read_offset += 0x10;
> +		msic->read_offset &= MSIC_FIFO_MASK;
> +
> +		cirq = irq_linear_revmap(msic->irq_host, msi);
> +		if (cirq != NO_IRQ)
> +			generic_handle_irq(cirq);
> +		else
> +			pr_debug("axon_msi: mapped to NO_IRQ!\n");
> +	}
> +
> +	dcr_write(msic->dcr_host, MSIC_READ_OFFSET_REG, msic->read_offset);
> +
> +	desc->chip->eoi(irq);
> +}
> +
> +static struct axon_msic *find_msi_translator(struct pci_dev *dev)
> +{
> +	struct irq_host *irq_host;
> +	struct device_node *np, *tmp;
> +	const phandle *ph;
> +
> +	np = pci_device_to_OF_node(dev);
> +	if (!np) {
> +		dev_dbg(&dev->dev, "axon_msi: no pci_dn found\n");
> +		return NULL;
> +	}
> +
> +	for (; np; tmp = of_get_parent(np), of_node_put(np), np = tmp) {
> +		ph = of_get_property(np, "msi-translator", NULL);
> +		if (ph)
> +			break;
> +	}
> +
> +	if (!ph) {
> +		dev_dbg(&dev->dev, "axon_msi: no msi-translator found\n");
> +		goto out_error;
> +	}
> +
> +	tmp = np;
> +	np = of_find_node_by_phandle(*ph);
> +	if (!np) {
> +		dev_dbg(&dev->dev, "axon_msi: invalid msi-translator found\n");
> +		goto out_error;
> +	}
> +
> +	irq_host = irq_find_host(np);
> +	if (!irq_host) {
> +		dev_dbg(&dev->dev, "axon_msi: no irq_host found\n");
> +		goto out_error;
> +	}
> +
> +	return irq_host->host_data;
> +
> +out_error:
> +	of_node_put(np);
> +	of_node_put(tmp);
> +
> +	return NULL;
> +}
> +
> +static int axon_msi_check_device(struct pci_dev *dev, int nvec, int type)
> +{
> +	if (!find_msi_translator(dev))
> +		return -ENODEV;
> +
> +	return 0;
> +}
> +
> +static int setup_msi_msg_address(struct pci_dev *dev, struct msi_msg *msg)
> +{
> +	struct device_node *np, *tmp;
> +	int pos, len;
> +	u16 flags;
> +	const u32 *prop;
> +
> +	np = pci_device_to_OF_node(dev);
> +	if (!np) {
> +		dev_dbg(&dev->dev, "axon_msi: no pci_dn found\n");
> +		return -ENODEV;
> +	}
> +
> +	pos = pci_find_capability(dev, PCI_CAP_ID_MSI);
> +	if (!pos || pci_read_config_word(dev, pos + PCI_MSI_FLAGS, &flags)) {
> +		dev_err(&dev->dev, "axon_msi: error reading config space!\n");
> +		return -EIO;
> +	}
> +
> +	for (; np; tmp = of_get_parent(np), of_node_put(np), np = tmp) {
> +		if (flags & PCI_MSI_FLAGS_64BIT) {
> +			prop = of_get_property(np, "msi-address-64", &len);
> +			if (prop)
> +				break;
> +		}
> +
> +		prop = of_get_property(np, "msi-address-32", &len);
> +		if (prop)
> +			break;
> +	}
> +	of_node_put(np);
> +
> +	if (!prop) {
> +		dev_dbg(&dev->dev, "axon_msi: no msi-address-(32|64) found\n");
> +		return -ENOENT;
> +	}
> +
> +	switch (len) {
> +	case 8:
> +		msg->address_hi = prop[0];
> +		msg->address_lo = prop[1];
> +		break;
> +	case 4:
> +		msg->address_hi = 0;
> +		msg->address_lo = prop[0];
> +		break;
> +	default:
> +		dev_dbg(&dev->dev, "axon_msi: malformed msi-address-(32|64)\n");
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static int axon_msi_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
> +{
> +	unsigned int virq, rc;
> +	struct msi_desc *entry;
> +	struct msi_msg msg;
> +	struct axon_msic *msic;
> +
> +	msic = find_msi_translator(dev);
> +	if (!msic)
> +		return -ENODEV;
> +
> +	rc = setup_msi_msg_address(dev, &msg);
> +	if (rc)
> +		return rc;
> +
> +	/* We rely on being able to stash a virq in a u16 */
> +	BUILD_BUG_ON(NR_IRQS > 65536);
> +
> +	list_for_each_entry(entry, &dev->msi_list, list) {
> +		virq = irq_create_direct_mapping(msic->irq_host);
> +		if (virq == NO_IRQ) {
> +			pr_debug("axon_msi: virq allocation failed!\n");
> +			return -1;
> +		}
> +		pr_debug("axon_msi: allocated virq 0x%x for %s\n",
> +			 virq, pci_name(dev));
> +
> +		set_irq_msi(virq, entry);
> +		msg.data = virq;
> +		write_msi_msg(virq, &msg);
> +	}
> +
> +	return 0;
> +}
> +
> +static void axon_msi_teardown_msi_irqs(struct pci_dev *dev)
> +{
> +	struct msi_desc *entry;
> +
> +	pr_debug("axon_msi: tearing down irqs for %s\n", pci_name(dev));
> +
> +	list_for_each_entry(entry, &dev->msi_list, list) {
> +		if (entry->irq == NO_IRQ)
> +			continue;
> +
> +		set_irq_msi(entry->irq, NULL);
> +		irq_dispose_mapping(entry->irq);
> +	}
> +}
> +
> +static struct irq_chip msic_irq_chip = {
> +	.mask		= mask_msi_irq,
> +	.unmask		= unmask_msi_irq,
> +	.shutdown	= unmask_msi_irq,
> +	.typename	= "AXON-MSI",
> +};
> +
> +static int msic_host_map(struct irq_host *h, unsigned int virq,
> +			 irq_hw_number_t hw)
> +{
> +	set_irq_chip_and_handler(virq, &msic_irq_chip, handle_simple_irq);
> +	return 0;
> +}
> +
> +static int msic_host_match(struct irq_host *host, struct device_node *node)
> +{
> +	struct axon_msic *msic = host->host_data;
> +	return msic->dn == node;
> +}
> +
> +static struct irq_host_ops msic_host_ops = {
> +	.match	= msic_host_match,
> +	.map	= msic_host_map,
> +};
> +
> +static int axon_msi_notify_reboot(struct notifier_block *nb,
> +				  unsigned long code, void *data)
> +{
> +	struct axon_msic *msic;
> +	u32 tmp;
> +
> +	list_for_each_entry(msic, &axon_msic_list, list) {
> +		tmp  = dcr_read(msic->dcr_host, MSIC_CTRL_REG_ADDR);
> +		tmp &= ~MSIC_ENABLE;
> +		dcr_write(msic->dcr_host, MSIC_CTRL_REG_ADDR, tmp);
> +		pr_debug("axon_msi: disabling %s\n", msic->dn->full_name);
> +	}
> +
> +	return 0;
> +}
> +
> +static struct notifier_block axon_msi_reboot_notifier = {
> +	.notifier_call = axon_msi_notify_reboot
> +};
> +
> +static int axon_msi_setup_one(struct device_node *node)
> +{
> +	struct page *page;
> +	struct axon_msic *msic;
> +	unsigned int virq;
> +
> +	pr_debug("axon_msi: setting up dn %s\n", node->full_name);
> +
> +	msic = kzalloc(sizeof(struct axon_msic), GFP_KERNEL);
> +	if (!msic) {
> +		printk(KERN_ERR "axon_msi: couldn't allocate msic\n");
> +		goto out_put;
> +	}
> +
> +	msic->dn = node;
> +
> +	msic->dcr_host = dcr_map(node, MSIC_DCR_BASE, MSIC_DCR_SIZE);
> +	if (!DCR_MAP_OK(msic->dcr_host)) {
> +		printk(KERN_ERR "axon_msi: dcr_map failed\n");
> +		goto out_free_msic;
> +	}
> +
> +	page = alloc_pages_node(of_node_to_nid(node),
> +				GFP_KERNEL, MSIC_FIFO_ORDER);
> +	if (!page) {
> +		printk(KERN_ERR "axon_msi: couldn't allocate fifo\n");
> +		goto out_free_msic;
> +	}
> +
> +	msic->fifo = page_address(page);
> +
> +	msic->irq_host = irq_alloc_host(IRQ_HOST_MAP_NOMAP, NR_IRQS,
> +					&msic_host_ops, 0);
> +	if (!msic->irq_host) {
> +		printk(KERN_ERR "axon_msi: couldn't allocate host\n");
> +		goto out_free_fifo;
> +	}
> +
> +	msic->irq_host->host_data = msic;
> +
> +	virq = irq_of_parse_and_map(node, 0);
> +	if (virq == NO_IRQ) {
> +		printk(KERN_ERR "axon_msi: irq parse/map failed!\n");
> +		goto out_free_host;
> +	}
> +
> +	set_irq_data(virq, msic);
> +	set_irq_chained_handler(virq, axon_msi_cascade);
> +	pr_debug("axon_msi: irq 0x%x setup for axon_msi!\n", virq);
> +
> +	/* Enable the MSIC hardware */
> +	dcr_write(msic->dcr_host, MSIC_BASE_ADDR_HI_REG,
> +				  (u64)msic->fifo >> 32);
> +	dcr_write(msic->dcr_host, MSIC_BASE_ADDR_LO_REG,
> +				  (u64)msic->fifo & 0xFFFFFFFF);
> +	dcr_write(msic->dcr_host, MSIC_CTRL_REG_ADDR,
> +				  MSIC_IRQ_ENABLE | MSIC_ENABLE |
> +				  (MSIC_FIFO_SIZE << MSIC_SIZE_SHIFT));
> +
> +	list_add(&msic->list, &axon_msic_list);
> +
> +	return 0;
> +
> +out_free_host:
> +	kfree(msic->irq_host);
> +out_free_fifo:
> +	__free_pages(virt_to_page(msic->fifo), MSIC_FIFO_ORDER);
> +out_free_msic:
> +	kfree(msic);
> +out_put:
> +	of_node_put(node);
> +
> +	return -1;
> +}
> +
> +static int axon_msi_init(void)
> +{
> +	struct device_node *node;
> +	int found = 0;
> +
> +	pr_debug("axon_msi: initialising ...\n");
> +
> +	for_each_compatible_node(node, NULL, "ibm,axon-msic") {
> +		if (axon_msi_setup_one(of_node_get(node)) == 0)
> +			found++;
> +	}
> +	of_node_put(node);
> +
> +	if (found) {
> +		ppc_md.setup_msi_irqs = axon_msi_setup_msi_irqs;
> +		ppc_md.teardown_msi_irqs = axon_msi_teardown_msi_irqs;
> +		ppc_md.msi_check_device = axon_msi_check_device;
> +
> +		register_reboot_notifier(&axon_msi_reboot_notifier);
> +
> +		pr_debug("axon_msi: registered callbacks!\n");
> +	}
> +
> +	return 0;
> +}
> +arch_initcall(axon_msi_init);

  parent reply	other threads:[~2007-06-04 22:45 UTC|newest]

Thread overview: 31+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2007-06-04 12:59 [PATCH 1/4] Split virq setup logic out into irq_setup_virq() Michael Ellerman
2007-06-04 13:00 ` [PATCH 2/4] Add irq_create_direct_mapping() Michael Ellerman
2007-06-04 22:44   ` Benjamin Herrenschmidt
2007-06-04 13:00 ` [PATCH 3/4] Add for_each_compatible_node() Michael Ellerman
2007-06-04 22:44   ` Benjamin Herrenschmidt
2007-06-04 13:00 ` [RFC/PATCH 4/4] Add support for MSI on Axon-based Cell systems Michael Ellerman
2007-06-04 17:09   ` Arnd Bergmann
2007-06-04 17:12   ` Arnd Bergmann
2007-06-08  2:53     ` Michael Ellerman
2007-06-08  8:47       ` Segher Boessenkool
2007-06-08 19:26         ` Arnd Bergmann
2007-06-08 19:36           ` Segher Boessenkool
2007-06-08 19:59             ` Segher Boessenkool
2007-06-08 20:06               ` Arnd Bergmann
2007-06-08 20:16                 ` Segher Boessenkool
2007-06-12  2:32                   ` Michael Ellerman
2007-06-08 20:01             ` Arnd Bergmann
2007-06-08 20:06               ` Segher Boessenkool
2007-06-12  2:30                 ` Michael Ellerman
2007-06-04 22:45   ` Benjamin Herrenschmidt [this message]
2007-06-05  7:00   ` Jean-Christophe Dubois
2007-06-05  9:16     ` Arnd Bergmann
2007-06-05  9:29       ` Jean-Christophe Dubois
2007-06-05  9:30       ` Arnd Bergmann
2007-06-07  7:58     ` Michael Ellerman
2007-06-07  8:10       ` Jean-Christophe Dubois
2007-06-07 11:55         ` Benjamin Herrenschmidt
2007-06-07 12:00           ` Jean-Christophe Dubois
2007-06-05 14:12   ` Milton Miller
2007-06-06  5:19     ` Michael Ellerman
2007-06-04 22:43 ` [PATCH 1/4] Split virq setup logic out into irq_setup_virq() Benjamin Herrenschmidt

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1180997115.31677.85.camel@localhost.localdomain \
    --to=benh@kernel.crashing.org \
    --cc=linuxppc-dev@ozlabs.org \
    --cc=michael@ellerman.id.au \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.