diff mbox

[v3,1/3] IRQ/Gic-V3: Add mbigen driver to support mbigen interrupt controller

Message ID 1436166548-34920-2-git-send-email-majun258@huawei.com (mailing list archive)
State New, archived
Headers show

Commit Message

majun (F) July 6, 2015, 7:09 a.m. UTC
This patch contains the mbigen interrupt controller driver.

To support Mbigen device, irq-mbigen.c and mbi.h are added.

As a kind of MSI interrupt controller, the mbigen is used as a child 
domain of ITS domain just like PCI devices.

In this patch:
[1]: Create the Mbigen domain as a child domain of ITS according to the
     Mbigen device node definition in dts file
[2]: Parse the interrupts of devices(connected to mbigen chip) node which defined in dts file 
[3]: other operations of interrupts: mask,unmask,activate..

Signed-off-by: Ma Jun <majun258@huawei.com>
---
 drivers/irqchip/Kconfig      |    8 +
 drivers/irqchip/Makefile     |    1 +
 drivers/irqchip/irq-mbigen.c |  486 ++++++++++++++++++++++++++++++++++++++++++
 include/linux/mbi.h          |   15 ++
 4 files changed, 510 insertions(+), 0 deletions(-)
 create mode 100644 drivers/irqchip/irq-mbigen.c
 create mode 100644 include/linux/mbi.h

Comments

Thomas Gleixner July 6, 2015, 12:33 p.m. UTC | #1
On Mon, 6 Jul 2015, Ma Jun wrote:

> This patch contains the mbigen interrupt controller driver.
> 
> To support Mbigen device, irq-mbigen.c and mbi.h are added.
> 
> As a kind of MSI interrupt controller, the mbigen is used as a child 
> domain of ITS domain just like PCI devices.
> 
> In this patch:
> [1]: Create the Mbigen domain as a child domain of ITS according to the
>      Mbigen device node definition in dts file
> [2]: Parse the interrupts of devices(connected to mbigen chip) node which defined in dts file 
> [3]: other operations of interrupts: mask,unmask,activate..

This is not a proper changelog. We can see which files are added and
modified.

What's missing is a proper description of MBI and how it's connected
to ITS.

Also the patches are in the wrong order. This one uses a function
which does not exist yet plus non existant device tree entries ....

> +config MBIGEN_IRQ_DOMAIN

The config symbol should reflect the functionality

    HISILICON_IRQ_MBIGEN

would be a more descriptive on, right?

> +	bool "Support mbigen interrupt controller"
> +	default y

Why would this be default Y? Nothing needs that stuff except hisilicon
platforms.

> +	depends on ARM_GIC_V3 && ARM_GIC_V3_ITS
> +	help
> +	 Enable the mbigen interrupt controller used on
> +	 Hisillicon platform.

Looks like a typo. Should that be Hisillycon perhaps?

> +/* Irq numbers per mbigen node supported */
> +#define IRQS_PER_MBIGEN_NODE	128
> +/* Max mbigen node number in one chip */
> +#define MG_NR			(10)

That define sucks. You have IRQS_PER_MBIGEN_NODE above so why not
using a descriptive one for this as well?

MBIGEN_NODES_PER_CHIP or something like that?

Also please use a proper name space. XXX_MBIGEN_ / MBIGEN_XXX / MB_XX
... looks just like created by a random generator.

> +/* Max interrupts  Mbigen chip supported */
> +#define MG_NR_IRQS		IRQS_PER_MBIGEN_NODE * (MG_NR + 1)

Why is this NODES per SOC plus 1? That's either wrong or the whole
logic of this define chain is wrong.

> +#define	DEV_SHIFT		(10)
> +#define	COMPOSE_HWIRQ(x, y)	(((x) << DEV_SHIFT) | (y))
> +#define	HWIRQ_OFFSET(x)		((x) & 0x3ff)

Magic hex constants pulled from thin air?

> +#define GET_NODE_NUM(x)		(((x) >> DEV_SHIFT) & 0xff)

Can you please use consistant spacing (tabs) for everything?

> +
> +#define IRQ_EVENT_ID_SHIFT	(12)
> +
> +#define IRQ_EVENT_ID_MASK	(0x3ff << IRQ_EVENT_ID_SHIFT)

More magic constants without explanation.

> +/* mbigen node register range */
> +#define MBIGEN_NODE_OFFSET	0x1000
> +/* vector register offset in mbigen node */
> +#define REG_MBIGEN_VEC_OFFSET	0x200
> +/* interrupt type register offset */
> +#define REG_MBIGEN_TYPE_OFFSET	0x0
> +
> +/* get the vector register addr in mbigne node

mbigne? Is that another variant?

> + * x: mbigen node number
> + * y: the irq pin offset
> + */
> +#define MBIGEN_NODE_ADDR_BASE(x)	((x) * MBIGEN_NODE_OFFSET)
> +
> +#define MBIGEN_VEC_REG_ADDR(x, y)	\
> +	(MBIGEN_NODE_ADDR_BASE(x) + REG_MBIGEN_VEC_OFFSET + ((y) * 4))
> +
> +#define MBIGEN_TYPE_REG_ADDR(x, y)	\
> +	(MBIGEN_NODE_ADDR_BASE(x) + REG_MBIGEN_TYPE_OFFSET + y)

These macros can be implemented cleanly as readable inline functions.

> +/**
> + * strutct mbigen_chip - mbigen chip structure descriptor

Broken kerneldoc comment. Also missing the struct member documentation

> + * usually one subsys(ex.DSA,ALG,PCIE)has one mbigen chip

Please use proper sentences.

> + */
> +struct mbigen_chip {
> +	raw_spinlock_t		lock;
> +	struct list_head	entry;
> +	struct device		*dev;
> +	struct device_node	*node;
> +	void __iomem		*base;
> +	struct irq_domain	*domain;
> +	struct list_head	nodes;
> +};
> +
> +/*
> + * mbigen_node: structure of mbigen node in a mbigen chip
> + * usually, a mbigen chip includes 8 ~ 11 mbigen nodes.
> + * The node number depends on the device number connected
> + * to this mbigen chip.
> + * @nid: the mbigen nod number

More broken comment.

> + */
> +struct mbigen_node {
> +	raw_spinlock_t		lock;
> +	struct list_head	entry;
> +	struct mbigen_chip	*chip;
> +	unsigned int		nid;
> +	struct list_head	nodes;
> +};
> +
> +/* refer to the devices connected to mbigen node */

Completely useless explanation of the data structure

> +struct mbigen_device {
> +	struct list_head	entry;
> +	struct mbigen_node	*mgn_node;
> +	struct device_node	*source;
> +	unsigned int		irq;
> +	unsigned int		nr_irqs;
> +	unsigned int		offset;
> +};
> +
> +/**
> + * struct mbi_desc - Message Based Interrupt (MBI) descriptor
> + *
> + * @dev:	the device owned the MBI
> + * @msg_id:	identifier of the message group
> + * @lines:	max number of interrupts supported by the message register
> + * @irq:	base linux interrupt number of the MBI
> + * @nvec:	number of interrupts controlled by the MBI
> + * @data:	message specific data
> + */
> +struct mbi_desc {
> +	struct device	*dev;
> +	int		msg_id;
> +	unsigned int	lines;

Please align the struct members proper.

> +	unsigned int		irq;
> +	unsigned int		nvec;
> +	void			*data;

Why is this a void pointer if that is message specific data?

> +};
> +
> +static LIST_HEAD(mbigen_chip_list);
> +static DEFINE_SPINLOCK(mbigen_lock);

What is the lock protecting and why does it need to be a spinlock? The
code completely lacks an explanation of the locking rules and the lock
nesting rules.

> +
> +static void mbigen_free_dev(struct mbigen_device *mgn_dev)
> +{
> +	raw_spin_lock(&mgn_dev->mgn_node->lock);
> +	list_del(&mgn_dev->entry);
> +	raw_spin_unlock(&mgn_dev->mgn_node->lock);
> +	kfree(mgn_dev);
> +}
> +
> +static struct mbigen_device *mbigen_create_device(struct mbigen_node *mgn_node,
> +					      struct device_node *node,
> +						  unsigned int virq,
> +					      unsigned int nr_irqs)

Your source formating is based on /dev/random, right?

> +{
> +	struct mbigen_device *mgn_dev;
> +
> +	mgn_dev = kzalloc(sizeof(*mgn_dev), GFP_KERNEL);
> +	if (!mgn_dev)
> +		return NULL;
> +
> +	INIT_LIST_HEAD(&mgn_dev->entry);
> +	mgn_dev->mgn_node = mgn_node;
> +	mgn_dev->source = node;
> +	mgn_dev->irq = virq;
> +	mgn_dev->nr_irqs = nr_irqs;
> +
> +	raw_spin_lock(&mgn_node->lock);
> +	list_add(&mgn_dev->entry, &mgn_node->nodes);
> +	raw_spin_unlock(&mgn_node->lock);
> +	return mgn_dev;
> +}
> +
> +static struct mbigen_node *get_mbigen_node(struct mbigen_chip *chip,
> +					unsigned int nid)
> +{
> +	struct mbigen_node *tmp, *mbigen;
> +	bool found = false;
> +
> +	if (nid > MG_NR) {
> +		pr_warn("MBIGEN: Device ID exceeds max number!!\n");
> +		return NULL;
> +	}
> +
> +	list_for_each_entry(mbigen, &chip->nodes, entry) {
> +		if (mbigen->nid == nid) {
> +			found = true;
> +			return mbigen;
> +		}
> +	}

That list walk does not need locking?

> +	/*
> +	 * Stop working if no memory available, even if we could
> +	 * get what we want.
> +	 */
> +	tmp = kzalloc(sizeof(*tmp), GFP_KERNEL);

tmp is a pretty useless variable name. Why cant you use mbigen for
that? Just because it makes the code harder to read, right?

> +	if (!tmp)
> +		return NULL;
> +
> +	raw_spin_lock(&chip->lock);

Your lock scopes are completely random. Why do you need to protect the
initialization of tmp?

> +	tmp->chip = chip;
> +	tmp->nid = nid;
> +	raw_spin_lock_init(&tmp->lock);
> +	INIT_LIST_HEAD(&tmp->entry);
> +	INIT_LIST_HEAD(&tmp->nodes);
> +
> +	list_add(&tmp->entry, &chip->nodes);
> +	mbigen = tmp;
> +	raw_spin_unlock(&chip->lock);
> +
> +	return mbigen;
> +}
> +
> +/**
> + * get_mbigen_node_type: get the mbigen node type
> + * @nid: the mbigen node value
> + * return 0: evnent id of interrupt connected to this node can be changed.
> + * return 1: evnent id of interrupt connected to this node cant be changed.
> + */
> +static int get_mbigen_node_type(int nid)
> +{
> +	if (nid > MG_NR) {
> +		pr_warn("MBIGEN: Device ID exceeds max number!\n");
> +		return 1;
> +	}
> +	if ((nid == 0) || (nid == 5) || (nid > 7))
> +		return 0;
> +	else
> +		return 1;

Oh no. We do not hardcode such properties into a driver. That wants to
be in the device tree and set as a property in the node data structure.

> +static int mbigen_write_msg(struct irq_data *d, struct msi_msg *msg)
> +{
> +	struct mbigen_chip *chip = d->domain->host_data;
> +	void __iomem *addr;
> +	u32 nid, val, offset;
> +	int ret = 0;
> +
> +	nid = GET_NODE_NUM(d->hwirq);
> +	ret = get_mbigen_node_type(nid);
> +	if (ret)
> +		return 0;

Care to explain what this does? It seems for some nodes you cannot
write the msi message. So how is that supposed to work? How is that
interrupt controlled (mask/unmask ...) ?

> +	offset = HWIRQ_OFFSET(d->hwirq);
> +
> +	addr = chip->base + MBIGEN_VEC_REG_ADDR(nid, offset);
> +
> +	val = readl_relaxed(addr);
> +
> +	val &= ~IRQ_EVENT_ID_MASK;
> +	val |= (msg->data << IRQ_EVENT_ID_SHIFT);
> +
> +	writel_relaxed(val, addr);
> +	return ret;
> +}
> +
> +/*
> + * Interrupt controller operations
> + */
> +static int mbigen_set_type(struct irq_data *d, unsigned int type)
> +{
> +	struct mbigen_chip *chip = d->domain->host_data;
> +	u32 ofst, mask;
> +	u32 val, nid, hwirq;
> +	void __iomem *addr;
> +
> +	if (type != IRQ_TYPE_LEVEL_HIGH && type != IRQ_TYPE_EDGE_RISING)
> +		return -EINVAL;
> +
> +	nid = GET_NODE_NUM(d->hwirq);
> +	hwirq = HWIRQ_OFFSET(d->hwirq);
> +
> +	ofst = hwirq / 32 * 4;
> +	mask = 1 << (hwirq % 32);
> +
> +	addr = chip->base + MBIGEN_TYPE_REG_ADDR(nid, ofst);
> +	raw_spin_lock(&chip->lock);
> +	val = readl_relaxed(addr);
> +
> +	if (type == IRQ_TYPE_LEVEL_HIGH)
> +		val |= mask;
> +	else if (type == IRQ_TYPE_EDGE_RISING)
> +		val &= ~mask;
> +
> +	writel_relaxed(val, addr);
> +	raw_spin_unlock(&chip->lock);

What is the lock protecting here? The read/write access to a per
interrupt register? Why is the per interrupt descriptor lock not
sufficient and why does the above write_msg not requited locking?

> +	return 0;
> +}
> +
> +static void mbigen_mask_irq(struct irq_data *data)
> +{
> +	irq_chip_mask_parent(data);
> +}
> +
> +static void mbigen_unmask_irq(struct irq_data *data)
> +{
> +	irq_chip_unmask_parent(data);
> +}
> +
> +static int mbigen_set_affinity(struct irq_data *data,
> +			       const struct cpumask *mask,
> +			       bool force)
> +{
> +	int ret;
> +
> +	ret = irq_chip_set_affinity_parent(data, mask, force);
> +	return ret;
> +}
> +
> +static void mbigen_irq_eoi(struct irq_data *d)
> +{
> +	irq_chip_eoi_parent(d);
> +}


What's the purpose of these wrappers? Why is setting the chip
functions to irq_chip_*_parent not sufficient?

> +static struct irq_chip mbigen_irq_chip = {
> +	.name			= "MBIGEN-v2",
> +	.irq_mask		= mbigen_mask_irq,
> +	.irq_unmask		= mbigen_unmask_irq,
> +	.irq_eoi		= mbigen_irq_eoi,
> +	.irq_set_affinity	= mbigen_set_affinity,
> +	.irq_set_type		= mbigen_set_type,
> +};

> +static int mbigen_domain_alloc(struct irq_domain *domain, unsigned int virq,
> +			       unsigned int nr_irqs, void *arg)
> +{
> +	struct mbigen_chip *chip = domain->host_data;
> +	struct of_phandle_args *irq_data = arg;
> +	irq_hw_number_t hwirq;
> +	u32 nid, dev_id, mbi_lines;
> +	struct mbigen_node *mgn_node;
> +	struct mbigen_device *mgn_dev;
> +	msi_alloc_info_t out_arg;
> +	int ret = 0, i;
> +
> +	/* OF style allocation, one interrupt at a time */

-ENOPARSE

> +	WARN_ON(nr_irqs != 1);
> +
> +	dev_id = irq_data->args[0];
> +	nid = irq_data->args[3];
> +	hwirq = COMPOSE_HWIRQ(nid, irq_data->args[2]);
> +
> +	mgn_node = get_mbigen_node(chip, nid);
> +	if (!mgn_node)
> +		return -ENODEV;
> +
> +	mgn_dev = mbigen_create_device(mgn_node, irq_data->np, virq, nr_irqs);
> +	if (!mgn_dev)
> +		return -ENOMEM;

Leaks the node allocation.

> +
> +	mbi_lines = irq_data->args[1];
> +
> +	ret = its_msi_prepare(domain, dev_id, mbi_lines, &out_arg);

This looks wrong. Why do you have an explicit call for this in the
allocation function?

msi_domain_ops.msi_prepare is called from the core code and you should
provide a msi_prepare callback which does the necessary initialization
and invokes the parent domains msi_prepare callback.

> +	if (ret)
> +		return ret;

Leaks the node allocation and the device.

> +
> +	ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, &out_arg);
> +	if (ret < 0)
> +		return ret;
> +
> +	for (i = 0; i < nr_irqs; i++) {

This loop is required because?

> +		irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i,
> +						&mbigen_irq_chip, mgn_dev);
> +	}
> +
> +	return ret;

> +/*
> + * Early initialization as an interrupt controller
> + */
> +static int __init mbigen_of_init(struct device_node *node,
> +				 struct device_node *parent_node)
> +{
> +	struct mbigen_chip *chip;
> +	struct irq_domain *parent_domain;
> +	int err;
> +
> +	parent_node = of_parse_phandle(node, "msi-parent", 0);

Huch. parent node is an argument here. So WHY do you need to override
it with some magic parent entry in the mbigen node? Seems your
devicetree design sucks.

> diff --git a/include/linux/mbi.h b/include/linux/mbi.h
> new file mode 100644
> index 0000000..d3b8155
> --- /dev/null
> +++ b/include/linux/mbi.h
> +
> +/* Function to parse and map message interrupts */
> +extern int its_msi_prepare(struct irq_domain *domain, u32 dev_id,
> +		    int nvec, msi_alloc_info_t *info);
> +extern struct irq_domain *get_its_domain(struct device_node *node);

Crap in various aspects

     - these functions should only be visible from drivers/irqchip/

     - the header name is wrong as it does not provide any MBI
       specific functionality

Thanks,

	tglx
majun (F) July 8, 2015, 4:21 a.m. UTC | #2
Hi Thomas:

? 2015/7/6 20:33, Thomas Gleixner ??:
> On Mon, 6 Jul 2015, Ma Jun wrote:
> 

>> +/**
>> + * get_mbigen_node_type: get the mbigen node type
>> + * @nid: the mbigen node value
>> + * return 0: evnent id of interrupt connected to this node can be changed.
>> + * return 1: evnent id of interrupt connected to this node cant be changed.
>> + */
>> +static int get_mbigen_node_type(int nid)
>> +{
>> +	if (nid > MG_NR) {
>> +		pr_warn("MBIGEN: Device ID exceeds max number!\n");
>> +		return 1;
>> +	}
>> +	if ((nid == 0) || (nid == 5) || (nid > 7))
>> +		return 0;
>> +	else
>> +		return 1;
> 
> Oh no. We do not hardcode such properties into a driver. That wants to
> be in the device tree and set as a property in the node data structure.
> 
Ok,I will move this to device tree

>> +static int mbigen_write_msg(struct irq_data *d, struct msi_msg *msg)
>> +{
>> +	struct mbigen_chip *chip = d->domain->host_data;
>> +	void __iomem *addr;
>> +	u32 nid, val, offset;
>> +	int ret = 0;
>> +
>> +	nid = GET_NODE_NUM(d->hwirq);
>> +	ret = get_mbigen_node_type(nid);
>> +	if (ret)
>> +		return 0;
> 
> Care to explain what this does? It seems for some nodes you cannot
> write the msi message. So how is that supposed to work? How is that
> interrupt controlled (mask/unmask ...) ?
> 
This function is used to write irq event id into vector register.Depends on
hardware design, write operation is permitted in some mbigen node(nid=0,5,and >7),
For other mbigen node, this register is read only.

But only vector register has this problem. Other registers are ok for read/write.

>> +
>> +	ofst = hwirq / 32 * 4;
>> +	mask = 1 << (hwirq % 32);
>> +
>> +	addr = chip->base + MBIGEN_TYPE_REG_ADDR(nid, ofst);
>> +	raw_spin_lock(&chip->lock);
>> +	val = readl_relaxed(addr);
>> +
>> +	if (type == IRQ_TYPE_LEVEL_HIGH)
>> +		val |= mask;
>> +	else if (type == IRQ_TYPE_EDGE_RISING)
>> +		val &= ~mask;
>> +
>> +	writel_relaxed(val, addr);
>> +	raw_spin_unlock(&chip->lock);
> 
> What is the lock protecting here? The read/write access to a per
> interrupt register? Why is the per interrupt descriptor lock not
> sufficient and why does the above write_msg not requited locking?
> 
yes,lock protecting is not necessary here.

>> +	return 0;
[...]
>> +static int mbigen_domain_alloc(struct irq_domain *domain, unsigned int virq,
>> +			       unsigned int nr_irqs, void *arg)
>> +{
>> +	struct mbigen_chip *chip = domain->host_data;
>> +	struct of_phandle_args *irq_data = arg;
>> +	irq_hw_number_t hwirq;
>> +	u32 nid, dev_id, mbi_lines;
>> +	struct mbigen_node *mgn_node;
>> +	struct mbigen_device *mgn_dev;
>> +	msi_alloc_info_t out_arg;
>> +	int ret = 0, i;
>> +
>> +	/* OF style allocation, one interrupt at a time */
> 
> -ENOPARSE
> 
what's this mean? I didn't find this definition in kernel code

>> +	WARN_ON(nr_irqs != 1);
>> +
>> +	dev_id = irq_data->args[0];
>> +	nid = irq_data->args[3];
>> +	hwirq = COMPOSE_HWIRQ(nid, irq_data->args[2]);
>> +
>> +	mgn_node = get_mbigen_node(chip, nid);
>> +	if (!mgn_node)
>> +		return -ENODEV;
>> +
>> +	mgn_dev = mbigen_create_device(mgn_node, irq_data->np, virq, nr_irqs);
>> +	if (!mgn_dev)
>> +		return -ENOMEM;
> 
> Leaks the node allocation.
> 
>> +
>> +	mbi_lines = irq_data->args[1];
>> +
>> +	ret = its_msi_prepare(domain, dev_id, mbi_lines, &out_arg);
> 
> This looks wrong. Why do you have an explicit call for this in the
> allocation function?
> 
> msi_domain_ops.msi_prepare is called from the core code and you should
> provide a msi_prepare callback which does the necessary initialization
> and invokes the parent domains msi_prepare callback.
> 
According to Marc suggestion, I changed the ITS code so I can use its_msi_prepare
function in my code.
So,do you mean i should not call this function directly ?
How about make this code likes below in mbigen driver:

static struct msi_domain_ops mbigen_domain_ops = {

	.msi_prepare	= mbigen_domain_ops_prepare,
};

static int mbigen_domain_ops_prepare(struct irq_domain *domain, struct device *dev,
			       int nvec, msi_alloc_info_t *info)
{
	return its_msi_prepare(domain, dev_id, count, info);
}


>> +	if (ret)
>> +		return ret;
> 
> Leaks the node allocation and the device.
> 
>> +
>> +	ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, &out_arg);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	for (i = 0; i < nr_irqs; i++) {
> 
> This loop is required because?
> 
Although we know this value is 1, I think use loop seems better

>> +		irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i,
>> +						&mbigen_irq_chip, mgn_dev);
>> +	}
>> +
>> +	return ret;
> 
>> +/*
>> + * Early initialization as an interrupt controller
>> + */
>> +static int __init mbigen_of_init(struct device_node *node,
>> +				 struct device_node *parent_node)
>> +{
>> +	struct mbigen_chip *chip;
>> +	struct irq_domain *parent_domain;
>> +	int err;
>> +
>> +	parent_node = of_parse_phandle(node, "msi-parent", 0);
> 
> Huch. parent node is an argument here. So WHY do you need to override
> it with some magic parent entry in the mbigen node? Seems your
> devicetree design sucks.
Because parent_nod argument point to gic node, but the parent device node of
mbigen is its node.

I didn't find the way how to pass its node into this function as the parent_node,
would you please give me some hint?
Thanks
> 
>> diff --git a/include/linux/mbi.h b/include/linux/mbi.h
>> new file mode 100644
>> index 0000000..d3b8155
>> --- /dev/null
>> +++ b/include/linux/mbi.h
>> +
>> +/* Function to parse and map message interrupts */
>> +extern int its_msi_prepare(struct irq_domain *domain, u32 dev_id,
>> +		    int nvec, msi_alloc_info_t *info);
>> +extern struct irq_domain *get_its_domain(struct device_node *node);
> 
> Crap in various aspects
> 
>      - these functions should only be visible from drivers/irqchip/
> 
>      - the header name is wrong as it does not provide any MBI
>        specific functionality
> 
Maybe I can named this file as 'arm-gic-v3-its.h' and put it in
include/linux/irqchip/

Thanks
Thomas Gleixner July 8, 2015, 10:44 a.m. UTC | #3
On Wed, 8 Jul 2015, majun (F) wrote:
> ? 2015/7/6 20:33, Thomas Gleixner ??:
> > Care to explain what this does? It seems for some nodes you cannot
> > write the msi message. So how is that supposed to work? How is that
> > interrupt controlled (mask/unmask ...) ?
> > 
> This function is used to write irq event id into vector register.Depends on
> hardware design, write operation is permitted in some mbigen node(nid=0,5,and >7),
> For other mbigen node, this register is read only.
> 
> But only vector register has this problem. Other registers are ok for read/write.

You still fail to explain how that works if the register is not
writeable. And the code wants a proper comment explaining it.
 
> >> +static int mbigen_domain_alloc(struct irq_domain *domain, unsigned int virq,
> >> +			       unsigned int nr_irqs, void *arg)
> >> +{
> >> +	struct mbigen_chip *chip = domain->host_data;
> >> +	struct of_phandle_args *irq_data = arg;
> >> +	irq_hw_number_t hwirq;
> >> +	u32 nid, dev_id, mbi_lines;
> >> +	struct mbigen_node *mgn_node;
> >> +	struct mbigen_device *mgn_dev;
> >> +	msi_alloc_info_t out_arg;
> >> +	int ret = 0, i;
> >> +
> >> +	/* OF style allocation, one interrupt at a time */
> > 
> > -ENOPARSE
> > 
> what's this mean? I didn't find this definition in kernel code

That error code does not exist at all. It's just a jargon word and
means: "Error: Cannot parse".

In other words: That comment does not make any sense to me.

> According to Marc suggestion, I changed the ITS code so I can use its_msi_prepare
> function in my code.
> So,do you mean i should not call this function directly ?
> How about make this code likes below in mbigen driver:
> 
> static struct msi_domain_ops mbigen_domain_ops = {
> 
> 	.msi_prepare	= mbigen_domain_ops_prepare,
> };
> 
> static int mbigen_domain_ops_prepare(struct irq_domain *domain, struct device *dev,
> 			       int nvec, msi_alloc_info_t *info)
> {
> 	return its_msi_prepare(domain, dev_id, count, info);
> }

How about using the parent domain pointer and invoking the function
via the parent->msi_domain_ops?

You seem to be focussed on hacking the ITS code into submission
instead of looking at the hierarchy information and use it proper.
 
> >> +	ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, &out_arg);
> >> +	if (ret < 0)
> >> +		return ret;
> >> +
> >> +	for (i = 0; i < nr_irqs; i++) {
> > 
> > This loop is required because?
> > 
> Although we know this value is 1, I think use loop seems better

Better for what? For obfuscating the code?

Either this function can handle nr_irqs > 1 or not. If it can handle
it, then the WARN_ON(nr_irqs != 1) is bogus. If it can not, then the
loop is pointless.

> >> +static int __init mbigen_of_init(struct device_node *node,
> >> +				 struct device_node *parent_node)
> >> +{
> >> +	struct mbigen_chip *chip;
> >> +	struct irq_domain *parent_domain;
> >> +	int err;
> >> +
> >> +	parent_node = of_parse_phandle(node, "msi-parent", 0);
> > 
> > Huch. parent node is an argument here. So WHY do you need to override
> > it with some magic parent entry in the mbigen node? Seems your
> > devicetree design sucks.
> Because parent_nod argument point to gic node, but the parent device node of
> mbigen is its node.
>
> I didn't find the way how to pass its node into this function as the parent_node,
> would you please give me some hint?

I gave you a hint already: 

> > .... Seems your devicetree design sucks.

In other words: If your device tree the MBI node parent is GIC, then
your device tree is not reflecting the actual hierarchy.

> > Crap in various aspects
> > 
> >      - these functions should only be visible from drivers/irqchip/
> > 
> >      - the header name is wrong as it does not provide any MBI
> >        specific functionality
> > 
> Maybe I can named this file as 'arm-gic-v3-its.h' and put it in
> include/linux/irqchip/

Care to read what I wrote?

> >      - these functions should only be visible from drivers/irqchip/

So what's the proper place for the header? Certainly not
include/linux/....

Aside of that, please look at the per-device MSI series Marc posted
(you were cc'ed). This is going to be where we are heading and your
driver should be based on that.

Thanks,

	tglx
Marc Zyngier July 8, 2015, 3:16 p.m. UTC | #4
On 08/07/15 05:21, majun (F) wrote:
> Hi Thomas:
> 
> ? 2015/7/6 20:33, Thomas Gleixner ??:
>> On Mon, 6 Jul 2015, Ma Jun wrote:
>>
> 
>>> +/**
>>> + * get_mbigen_node_type: get the mbigen node type
>>> + * @nid: the mbigen node value
>>> + * return 0: evnent id of interrupt connected to this node can be changed.
>>> + * return 1: evnent id of interrupt connected to this node cant be changed.
>>> + */
>>> +static int get_mbigen_node_type(int nid)
>>> +{
>>> +	if (nid > MG_NR) {
>>> +		pr_warn("MBIGEN: Device ID exceeds max number!\n");
>>> +		return 1;
>>> +	}
>>> +	if ((nid == 0) || (nid == 5) || (nid > 7))
>>> +		return 0;
>>> +	else
>>> +		return 1;
>>
>> Oh no. We do not hardcode such properties into a driver. That wants to
>> be in the device tree and set as a property in the node data structure.
>>
> Ok,I will move this to device tree
> 
>>> +static int mbigen_write_msg(struct irq_data *d, struct msi_msg *msg)
>>> +{
>>> +	struct mbigen_chip *chip = d->domain->host_data;
>>> +	void __iomem *addr;
>>> +	u32 nid, val, offset;
>>> +	int ret = 0;
>>> +
>>> +	nid = GET_NODE_NUM(d->hwirq);
>>> +	ret = get_mbigen_node_type(nid);
>>> +	if (ret)
>>> +		return 0;
>>
>> Care to explain what this does? It seems for some nodes you cannot
>> write the msi message. So how is that supposed to work? How is that
>> interrupt controlled (mask/unmask ...) ?
>>
> This function is used to write irq event id into vector register.Depends on
> hardware design, write operation is permitted in some mbigen node(nid=0,5,and >7),
> For other mbigen node, this register is read only.

So how do you expect this to work? You cannot program the event
generated by the mbigen, and the ITS has an ITT that probably doesn't
match your HW.

Best case, the interrupt is simply dropped, worse case you end up in an
interrupt storm because you can't figure out which device is screaming.

I'm a bit puzzled.

	M.
Marc Zyngier July 8, 2015, 3:30 p.m. UTC | #5
Hi,

Aside from all the comments Thomas had, the following aspect is worrying
me a bit:

On 06/07/15 08:09, Ma Jun wrote:
> This patch contains the mbigen interrupt controller driver.

[...]

> +static int mbigen_set_type(struct irq_data *d, unsigned int type)
> +{
> +       struct mbigen_chip *chip = d->domain->host_data;
> +       u32 ofst, mask;
> +       u32 val, nid, hwirq;
> +       void __iomem *addr;
> +
> +       if (type != IRQ_TYPE_LEVEL_HIGH && type != IRQ_TYPE_EDGE_RISING)
> +               return -EINVAL;

You seem to be supporting both edge and level triggered interrupts.

Given that the ITS is edge triggered only, I must assume you have some
code to regenerate an edge if the wired interrupt is level triggered,
and that the line level is still high when you perform the EOI...

> +
> +       nid = GET_NODE_NUM(d->hwirq);
> +       hwirq = HWIRQ_OFFSET(d->hwirq);
> +
> +       ofst = hwirq / 32 * 4;
> +       mask = 1 << (hwirq % 32);
> +
> +       addr = chip->base + MBIGEN_TYPE_REG_ADDR(nid, ofst);
> +       raw_spin_lock(&chip->lock);
> +       val = readl_relaxed(addr);
> +
> +       if (type == IRQ_TYPE_LEVEL_HIGH)
> +               val |= mask;
> +       else if (type == IRQ_TYPE_EDGE_RISING)
> +               val &= ~mask;
> +
> +       writel_relaxed(val, addr);
> +       raw_spin_unlock(&chip->lock);
> +
> +       return 0;
> +}
> +
> +static void mbigen_mask_irq(struct irq_data *data)
> +{
> +       irq_chip_mask_parent(data);
> +}
> +
> +static void mbigen_unmask_irq(struct irq_data *data)
> +{
> +       irq_chip_unmask_parent(data);
> +}
> +
> +static int mbigen_set_affinity(struct irq_data *data,
> +                              const struct cpumask *mask,
> +                              bool force)
> +{
> +       int ret;
> +
> +       ret = irq_chip_set_affinity_parent(data, mask, force);
> +       return ret;
> +}
> +
> +static void mbigen_irq_eoi(struct irq_data *d)
> +{
> +       irq_chip_eoi_parent(d);

... but this function doesn't have any code dealing with injecting an
edge on detecting a level high.

So how does it work? Either you're missing some logic here, or you don't
really support level interrupts.

Thanks,

	M.
majun (F) July 16, 2015, 8:35 a.m. UTC | #6
? 2015/7/8 23:16, Marc Zyngier ??:
> On 08/07/15 05:21, majun (F) wrote:
>> Hi Thomas:
>>
[...]
>>>> +
>>>> +	nid = GET_NODE_NUM(d->hwirq);
>>>> +	ret = get_mbigen_node_type(nid);
>>>> +	if (ret)
>>>> +		return 0;
>>>
>>> Care to explain what this does? It seems for some nodes you cannot
>>> write the msi message. So how is that supposed to work? How is that
>>> interrupt controlled (mask/unmask ...) ?
>>>
>> This function is used to write irq event id into vector register.Depends on
>> hardware design, write operation is permitted in some mbigen node(nid=0,5,and >7),
>> For other mbigen node, this register is read only.
> 
> So how do you expect this to work? You cannot program the event
> generated by the mbigen, and the ITS has an ITT that probably doesn't
> match your HW.
> 
> Best case, the interrupt is simply dropped, worse case you end up in an
> interrupt storm because you can't figure out which device is screaming.
> 
> I'm a bit puzzled.

For interrupts connect to mbigen , the interrupt trigger type, device id and
event id value are encoded in mbigen chip already.

There are two types of mbigen node within a mbigen chip.
Type1: event id valud can't be programmed.
Type2: event id value can be programmed.

For example: An device with 5 interrupts connected to Mbigen node
type 1.The default event id vlaue encoded in mbigen chip for these 5 interrupt
is from 0 to 4.

Because the event id value can't be programmed, we need to define all of
5 interrupts in dts file so that these 5 interrupt has
majun (F) July 16, 2015, 8:35 a.m. UTC | #7
? 2015/7/8 18:44, Thomas Gleixner ??:
> On Wed, 8 Jul 2015, majun (F) wrote:
>> ? 2015/7/6 20:33, Thomas Gleixner ??:
>>> Care to explain what this does? It seems for some nodes you cannot
>>> write the msi message. So how is that supposed to work? How is that
>>> interrupt controlled (mask/unmask ...) ?
>>>
>> This function is used to write irq event id into vector register.Depends on
>> hardware design, write operation is permitted in some mbigen node(nid=0,5,and >7),
>> For other mbigen node, this register is read only.
>>
>> But only vector register has this problem. Other registers are ok for read/write.
> 
> You still fail to explain how that works if the register is not
> writeable. And the code wants a proper comment explaining it.
> 

Actually, the interrupt trigger type, device id and event id already encoded in
mbigen chip.

Depends on hardweare design, the device id and event id value in some mbigen nodes
can't be modified, but some nodes can.

For the mbigen node which event id can't be modified, we can use the default event id
value (encoded in mbigen register).
If the event id can be programmed, we can use this function to modify the event id value.

[....]
> 
> Aside of that, please look at the per-device MSI series Marc posted
> (you were cc'ed). This is going to be where we are heading and your
> driver should be based on that.
Ok, i will  change mbigen code based on Marc's patch.
> 
> Thanks,
> 
> 	tglx
>
majun (F) July 16, 2015, 8:35 a.m. UTC | #8
? 2015/7/8 23:30, Marc Zyngier ??:
> Hi,
> 
> Aside from all the comments Thomas had, the following aspect is worrying
> me a bit:
> 
> On 06/07/15 08:09, Ma Jun wrote:
>> This patch contains the mbigen interrupt controller driver.
> 
> [...]
> 
>> +static int mbigen_set_type(struct irq_data *d, unsigned int type)
>> +{
>> +       struct mbigen_chip *chip = d->domain->host_data;
>> +       u32 ofst, mask;
>> +       u32 val, nid, hwirq;
>> +       void __iomem *addr;
>> +
>> +       if (type != IRQ_TYPE_LEVEL_HIGH && type != IRQ_TYPE_EDGE_RISING)
>> +               return -EINVAL;
> 
> You seem to be supporting both edge and level triggered interrupts.
> 
> Given that the ITS is edge triggered only, I must assume you have some
> code to regenerate an edge if the wired interrupt is level triggered,
> and that the line level is still high when you perform the EOI...
> 
For each interrupt, there is state machine in mbigen chip.
inactive-->pending--> active(pending & active)

The level triggered interrupt process flow list as below:
device---->mbigen---->ITS---->GIC--->CPU

[1]: device triggered interrupt A and line level changes to high
[2]: Mbigen receive interrupt A and changes the status of A to pending in mbigen(mbigen.state = pending)
[3]: Mbigen send interrupt A to ITS , the A status in mbigen will be changed to pending
	& active (mbigen.state = pending & active)
[4]: ITS receive the interrupt A and send A to gic (A status in gic is pending. gic.state=pending)
[5]: CPU ack the interrupt A ( gic.state = active)
[6]: Enter interrupt handler. The interrupt line level is cleared in device irq handler.
[7]: When detects the low level on interrupt A line, mbigen change the interrupt A status
	from pending & active to inactive (mbigen.state = inactive).
[8]: Send EOI . a): write register to clear the status in mbigen .
	b):clear the status in gic. (gic.state = inactive)

[....]
>> +static void mbigen_irq_eoi(struct irq_data *d)
>> +{
>> +       irq_chip_eoi_parent(d);
> 
> ... but this function doesn't have any code dealing with injecting an
> edge on detecting a level high.
> 

Yes, before irq_chip_eoi_parent is called, some code should be added to
clear the interrupt status in mbigen.

> So how does it work? Either you're missing some logic here, or you don't
> really support level interrupts.
> 
> Thanks,
> 
> 	M.
>
Marc Zyngier July 16, 2015, 8:52 a.m. UTC | #9
On 16/07/15 09:35, majun (F) wrote:
> 
> 
> ? 2015/7/8 23:16, Marc Zyngier ??:
>> On 08/07/15 05:21, majun (F) wrote:
>>> Hi Thomas:
>>>
> [...]
>>>>> +
>>>>> +	nid = GET_NODE_NUM(d->hwirq);
>>>>> +	ret = get_mbigen_node_type(nid);
>>>>> +	if (ret)
>>>>> +		return 0;
>>>>
>>>> Care to explain what this does? It seems for some nodes you cannot
>>>> write the msi message. So how is that supposed to work? How is that
>>>> interrupt controlled (mask/unmask ...) ?
>>>>
>>> This function is used to write irq event id into vector register.Depends on
>>> hardware design, write operation is permitted in some mbigen node(nid=0,5,and >7),
>>> For other mbigen node, this register is read only.
>>
>> So how do you expect this to work? You cannot program the event
>> generated by the mbigen, and the ITS has an ITT that probably doesn't
>> match your HW.
>>
>> Best case, the interrupt is simply dropped, worse case you end up in an
>> interrupt storm because you can't figure out which device is screaming.
>>
>> I'm a bit puzzled.
> 
> For interrupts connect to mbigen , the interrupt trigger type, device id and
> event id value are encoded in mbigen chip already.
> 
> There are two types of mbigen node within a mbigen chip.
> Type1: event id valud can't be programmed.
> Type2: event id value can be programmed.
> 
> For example: An device with 5 interrupts connected to Mbigen node
> type 1.The default event id vlaue encoded in mbigen chip for these 5 interrupt
> is from 0 to 4.
> 
> Because the event id value can't be programmed, we need to define all of
> 5 interrupts in dts file so that these 5 interrupt has

You can define what you want in the device tree, the ITS doesn't care!
Nothing in the ITS code parses this property, and there is absolutely
zero chance that the even the ITS has allocated will actually match what
you expect.

The ITS *relies* on the principle that the evenID can be programmed,
just like any MSI controller relies on the device to be programmed with
whatever payload has been provided. If all of a sudden we have to
support HW that has its own view of the payload, what you have here will
simply not work.

	M.
majun (F) July 16, 2015, 9:22 a.m. UTC | #10
? 2015/7/16 16:52, Marc Zyngier ??:
> On 16/07/15 09:35, majun (F) wrote:

>>> I'm a bit puzzled.
>>
>> For interrupts connect to mbigen , the interrupt trigger type, device id and
>> event id value are encoded in mbigen chip already.
>>
>> There are two types of mbigen node within a mbigen chip.
>> Type1: event id valud can't be programmed.
>> Type2: event id value can be programmed.
>>
>> For example: An device with 5 interrupts connected to Mbigen node
>> type 1.The default event id vlaue encoded in mbigen chip for these 5 interrupt
>> is from 0 to 4.
>>
>> Because the event id value can't be programmed, we need to define all of
>> 5 interrupts in dts file so that these 5 interrupt has
> 
> You can define what you want in the device tree, the ITS doesn't care!
> Nothing in the ITS code parses this property, and there is absolutely
> zero chance that the even the ITS has allocated will actually match what
> you expect.
> 
> The ITS *relies* on the principle that the evenID can be programmed,
> just like any MSI controller relies on the device to be programmed with
> whatever payload has been provided. If all of a sudden we have to
> support HW that has its own view of the payload, what you have here will
> simply not work.
> 
"If all of a sudden we have to
 support HW that has its own view of the payload, what you have here will
 simply not work."

I am not very unstand this case, would you please explain this more detail?
Thanks!
Marc Zyngier July 16, 2015, 9:30 a.m. UTC | #11
On 16/07/15 10:22, majun (F) wrote:
> 
> 
> ? 2015/7/16 16:52, Marc Zyngier ??:
>> On 16/07/15 09:35, majun (F) wrote:
> 
>>>> I'm a bit puzzled.
>>>
>>> For interrupts connect to mbigen , the interrupt trigger type, device id and
>>> event id value are encoded in mbigen chip already.
>>>
>>> There are two types of mbigen node within a mbigen chip.
>>> Type1: event id valud can't be programmed.
>>> Type2: event id value can be programmed.
>>>
>>> For example: An device with 5 interrupts connected to Mbigen node
>>> type 1.The default event id vlaue encoded in mbigen chip for these 5 interrupt
>>> is from 0 to 4.
>>>
>>> Because the event id value can't be programmed, we need to define all of
>>> 5 interrupts in dts file so that these 5 interrupt has
>>
>> You can define what you want in the device tree, the ITS doesn't care!
>> Nothing in the ITS code parses this property, and there is absolutely
>> zero chance that the even the ITS has allocated will actually match what
>> you expect.
>>
>> The ITS *relies* on the principle that the evenID can be programmed,
>> just like any MSI controller relies on the device to be programmed with
>> whatever payload has been provided. If all of a sudden we have to
>> support HW that has its own view of the payload, what you have here will
>> simply not work.
>>
> "If all of a sudden we have to
>  support HW that has its own view of the payload, what you have here will
>  simply not work."
> 
> I am not very unstand this case, would you please explain this more detail?

It is the ITS driver that allocates the eventID, not the device. If your
hardware has decided that it will use event 5, while the ITS expect it
to use event 3, nothing will work. And there is no way for the ITS to
know which event your device is using (it doesn't parse your device's
device tree).

	M.
majun (F) July 16, 2015, 12:26 p.m. UTC | #12
? 2015/7/16 17:30, Marc Zyngier ??:
> On 16/07/15 10:22, majun (F) wrote:
>>
>>
>> ? 2015/7/16 16:52, Marc Zyngier ??:
>>> On 16/07/15 09:35, majun (F) wrote:
>>
>>>>> I'm a bit puzzled.
>>>>
>>>> For interrupts connect to mbigen , the interrupt trigger type, device id and
>>>> event id value are encoded in mbigen chip already.
>>>>
>>>> There are two types of mbigen node within a mbigen chip.
>>>> Type1: event id valud can't be programmed.
>>>> Type2: event id value can be programmed.
>>>>
>>>> For example: An device with 5 interrupts connected to Mbigen node
>>>> type 1.The default event id vlaue encoded in mbigen chip for these 5 interrupt
>>>> is from 0 to 4.
>>>>
>>>> Because the event id value can't be programmed, we need to define all of
>>>> 5 interrupts in dts file so that these 5 interrupt has
>>>
>>> You can define what you want in the device tree, the ITS doesn't care!
>>> Nothing in the ITS code parses this property, and there is absolutely
>>> zero chance that the even the ITS has allocated will actually match what
>>> you expect.
>>>
>>> The ITS *relies* on the principle that the evenID can be programmed,
>>> just like any MSI controller relies on the device to be programmed with
>>> whatever payload has been provided. If all of a sudden we have to
>>> support HW that has its own view of the payload, what you have here will
>>> simply not work.
>>>
>> "If all of a sudden we have to
>>  support HW that has its own view of the payload, what you have here will
>>  simply not work."
>>
>> I am not very unstand this case, would you please explain this more detail?
> 
> It is the ITS driver that allocates the eventID, not the device. If your
> hardware has decided that it will use event 5, while the ITS expect it
> to use event 3, nothing will work. And there is no way for the ITS to
> know which event your device is using (it doesn't parse your device's
> device tree).
> 
Yes, I agree with you that eventID should be allocated by ITS driver.
But I want to explain the case you said above can be avoided.

For example, An device with total 5 interrupts connected to Mbigen node.
In mbigen chip, the default eventID value for each device starts from 0.
So, for these 5 interrupts the default eventID value is from 0 to 4.

Because the default eventID is fixed in mbigen chip, to make these 5 interrupt
work, the only way is define all these 5 interrupts in dts file.

When irq initializing, ITS driver will allocat LPI interrupt number for these
5 interrupts, for example : from 8192 to 8196. and the eventID value is from 0 to 4.
Now, the allocated eventID value same as the eventID value encoded in mbigen chip,
The interrupt can work.

I know this is not a good method,but for current mbigen chip,it's the only solution.
Marc Zyngier July 16, 2015, 1:37 p.m. UTC | #13
On 16/07/15 13:26, majun (F) wrote:
> 
> 
> ? 2015/7/16 17:30, Marc Zyngier ??:
>> On 16/07/15 10:22, majun (F) wrote:
>>>
>>>
>>> ? 2015/7/16 16:52, Marc Zyngier ??:
>>>> On 16/07/15 09:35, majun (F) wrote:
>>>
>>>>>> I'm a bit puzzled.
>>>>>
>>>>> For interrupts connect to mbigen , the interrupt trigger type, device id and
>>>>> event id value are encoded in mbigen chip already.
>>>>>
>>>>> There are two types of mbigen node within a mbigen chip.
>>>>> Type1: event id valud can't be programmed.
>>>>> Type2: event id value can be programmed.
>>>>>
>>>>> For example: An device with 5 interrupts connected to Mbigen node
>>>>> type 1.The default event id vlaue encoded in mbigen chip for these 5 interrupt
>>>>> is from 0 to 4.
>>>>>
>>>>> Because the event id value can't be programmed, we need to define all of
>>>>> 5 interrupts in dts file so that these 5 interrupt has
>>>>
>>>> You can define what you want in the device tree, the ITS doesn't care!
>>>> Nothing in the ITS code parses this property, and there is absolutely
>>>> zero chance that the even the ITS has allocated will actually match what
>>>> you expect.
>>>>
>>>> The ITS *relies* on the principle that the evenID can be programmed,
>>>> just like any MSI controller relies on the device to be programmed with
>>>> whatever payload has been provided. If all of a sudden we have to
>>>> support HW that has its own view of the payload, what you have here will
>>>> simply not work.
>>>>
>>> "If all of a sudden we have to
>>>  support HW that has its own view of the payload, what you have here will
>>>  simply not work."
>>>
>>> I am not very unstand this case, would you please explain this more detail?
>>
>> It is the ITS driver that allocates the eventID, not the device. If your
>> hardware has decided that it will use event 5, while the ITS expect it
>> to use event 3, nothing will work. And there is no way for the ITS to
>> know which event your device is using (it doesn't parse your device's
>> device tree).
>>
> Yes, I agree with you that eventID should be allocated by ITS driver.
> But I want to explain the case you said above can be avoided.
> 
> For example, An device with total 5 interrupts connected to Mbigen node.
> In mbigen chip, the default eventID value for each device starts from 0.
> So, for these 5 interrupts the default eventID value is from 0 to 4.
> 
> Because the default eventID is fixed in mbigen chip, to make these 5 interrupt
> work, the only way is define all these 5 interrupts in dts file.

But the ITS doesn't read these interrupts, and won't let you program the
translation either.

> When irq initializing, ITS driver will allocat LPI interrupt number for these
> 5 interrupts, for example : from 8192 to 8196. and the eventID value is from 0 to 4.
> Now, the allocated eventID value same as the eventID value encoded in mbigen chip,
> The interrupt can work.

But that's pure luck! What if I decide to change the allocation method
so that all the devices share a common ITT?

Have you really hardcoded the Linux behaviour in your hardware?

> I know this is not a good method,but for current mbigen chip,it's the only solution.

I'm sorry, I can't call this a solution.

	M.
majun (F) July 27, 2015, 2:25 a.m. UTC | #14
Hi Marc

? 2015/7/16 21:37, Marc Zyngier ??:
[...]
>>
>> For example, An device with total 5 interrupts connected to Mbigen node.
>> In mbigen chip, the default eventID value for each device starts from 0.
>> So, for these 5 interrupts the default eventID value is from 0 to 4.
>>
>> Because the default eventID is fixed in mbigen chip, to make these 5 interrupt
>> work, the only way is define all these 5 interrupts in dts file.
> 
> But the ITS doesn't read these interrupts, and won't let you program the
> translation either.
> 
>> When irq initializing, ITS driver will allocat LPI interrupt number for these
>> 5 interrupts, for example : from 8192 to 8196. and the eventID value is from 0 to 4.
>> Now, the allocated eventID value same as the eventID value encoded in mbigen chip,
>> The interrupt can work.
> 
> But that's pure luck! What if I decide to change the allocation method
> so that all the devices share a common ITT?
> 
> Have you really hardcoded the Linux behaviour in your hardware?
> 
>> I know this is not a good method,but for current mbigen chip,it's the only solution.
> 
> I'm sorry, I can't call this a solution.
Yes,you are right. This is not a right way to solve the problem.
This problem will be fixed in next version chip.

> 
> 	M.
>
diff mbox

Patch

diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index 6de62a9..bb70af7 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -27,6 +27,14 @@  config ARM_GIC_V3_ITS
 	bool
 	select PCI_MSI_IRQ_DOMAIN
 
+config MBIGEN_IRQ_DOMAIN
+	bool "Support mbigen interrupt controller"
+	default y
+	depends on ARM_GIC_V3 && ARM_GIC_V3_ITS
+	help
+	 Enable the mbigen interrupt controller used on
+	 Hisillicon platform.
+
 config ARM_NVIC
 	bool
 	select IRQ_DOMAIN
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index dda4927..23571c1 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -23,6 +23,7 @@  obj-$(CONFIG_ARM_GIC)			+= irq-gic.o irq-gic-common.o
 obj-$(CONFIG_ARM_GIC_V2M)		+= irq-gic-v2m.o
 obj-$(CONFIG_ARM_GIC_V3)		+= irq-gic-v3.o irq-gic-common.o
 obj-$(CONFIG_ARM_GIC_V3_ITS)		+= irq-gic-v3-its.o
+obj-$(CONFIG_MBIGEN_IRQ_DOMAIN)		+= irq-mbigen.o
 obj-$(CONFIG_ARM_NVIC)			+= irq-nvic.o
 obj-$(CONFIG_ARM_VIC)			+= irq-vic.o
 obj-$(CONFIG_ATMEL_AIC_IRQ)		+= irq-atmel-aic-common.o irq-atmel-aic.o
diff --git a/drivers/irqchip/irq-mbigen.c b/drivers/irqchip/irq-mbigen.c
new file mode 100644
index 0000000..25f1442
--- /dev/null
+++ b/drivers/irqchip/irq-mbigen.c
@@ -0,0 +1,486 @@ 
+/*
+ * Copyright (C) 2014 Hisilicon Limited, All Rights Reserved.
+ * Author: Yun Wu <wuyun.wu@huawei.com>
+ * Author: Jun Ma <majun258@huawei.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/mbi.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/msi.h>
+#include "irqchip.h"
+
+/* Irq numbers per mbigen node supported */
+#define IRQS_PER_MBIGEN_NODE	128
+/* Max mbigen node number in one chip */
+#define MG_NR			(10)
+/* Max interrupts  Mbigen chip supported */
+#define MG_NR_IRQS		IRQS_PER_MBIGEN_NODE * (MG_NR + 1)
+
+#define	DEV_SHIFT		(10)
+#define	COMPOSE_HWIRQ(x, y)	(((x) << DEV_SHIFT) | (y))
+#define	HWIRQ_OFFSET(x)		((x) & 0x3ff)
+#define GET_NODE_NUM(x)		(((x) >> DEV_SHIFT) & 0xff)
+
+#define IRQ_EVENT_ID_SHIFT	(12)
+
+#define IRQ_EVENT_ID_MASK	(0x3ff << IRQ_EVENT_ID_SHIFT)
+
+/* mbigen node register range */
+#define MBIGEN_NODE_OFFSET	0x1000
+/* vector register offset in mbigen node */
+#define REG_MBIGEN_VEC_OFFSET	0x200
+/* interrupt type register offset */
+#define REG_MBIGEN_TYPE_OFFSET	0x0
+
+/* get the vector register addr in mbigne node
+ * x: mbigen node number
+ * y: the irq pin offset
+ */
+#define MBIGEN_NODE_ADDR_BASE(x)	((x) * MBIGEN_NODE_OFFSET)
+
+#define MBIGEN_VEC_REG_ADDR(x, y)	\
+	(MBIGEN_NODE_ADDR_BASE(x) + REG_MBIGEN_VEC_OFFSET + ((y) * 4))
+
+#define MBIGEN_TYPE_REG_ADDR(x, y)	\
+	(MBIGEN_NODE_ADDR_BASE(x) + REG_MBIGEN_TYPE_OFFSET + y)
+
+/**
+ * strutct mbigen_chip - mbigen chip structure descriptor
+ * usually one subsys(ex.DSA,ALG,PCIE)has one mbigen chip
+ */
+struct mbigen_chip {
+	raw_spinlock_t		lock;
+	struct list_head	entry;
+	struct device		*dev;
+	struct device_node	*node;
+	void __iomem		*base;
+	struct irq_domain	*domain;
+	struct list_head	nodes;
+};
+
+/*
+ * mbigen_node: structure of mbigen node in a mbigen chip
+ * usually, a mbigen chip includes 8 ~ 11 mbigen nodes.
+ * The node number depends on the device number connected
+ * to this mbigen chip.
+ * @nid: the mbigen nod number
+ */
+struct mbigen_node {
+	raw_spinlock_t		lock;
+	struct list_head	entry;
+	struct mbigen_chip	*chip;
+	unsigned int		nid;
+	struct list_head	nodes;
+};
+
+/* refer to the devices connected to mbigen node */
+struct mbigen_device {
+	struct list_head	entry;
+	struct mbigen_node	*mgn_node;
+	struct device_node	*source;
+	unsigned int		irq;
+	unsigned int		nr_irqs;
+	unsigned int		offset;
+};
+
+/**
+ * struct mbi_desc - Message Based Interrupt (MBI) descriptor
+ *
+ * @dev:	the device owned the MBI
+ * @msg_id:	identifier of the message group
+ * @lines:	max number of interrupts supported by the message register
+ * @irq:	base linux interrupt number of the MBI
+ * @nvec:	number of interrupts controlled by the MBI
+ * @data:	message specific data
+ */
+struct mbi_desc {
+	struct device	*dev;
+	int		msg_id;
+	unsigned int	lines;
+	unsigned int		irq;
+	unsigned int		nvec;
+	void			*data;
+};
+
+static LIST_HEAD(mbigen_chip_list);
+static DEFINE_SPINLOCK(mbigen_lock);
+
+static void mbigen_free_dev(struct mbigen_device *mgn_dev)
+{
+	raw_spin_lock(&mgn_dev->mgn_node->lock);
+	list_del(&mgn_dev->entry);
+	raw_spin_unlock(&mgn_dev->mgn_node->lock);
+	kfree(mgn_dev);
+}
+
+static struct mbigen_device *mbigen_create_device(struct mbigen_node *mgn_node,
+					      struct device_node *node,
+						  unsigned int virq,
+					      unsigned int nr_irqs)
+{
+	struct mbigen_device *mgn_dev;
+
+	mgn_dev = kzalloc(sizeof(*mgn_dev), GFP_KERNEL);
+	if (!mgn_dev)
+		return NULL;
+
+	INIT_LIST_HEAD(&mgn_dev->entry);
+	mgn_dev->mgn_node = mgn_node;
+	mgn_dev->source = node;
+	mgn_dev->irq = virq;
+	mgn_dev->nr_irqs = nr_irqs;
+
+	raw_spin_lock(&mgn_node->lock);
+	list_add(&mgn_dev->entry, &mgn_node->nodes);
+	raw_spin_unlock(&mgn_node->lock);
+	return mgn_dev;
+}
+
+static struct mbigen_node *get_mbigen_node(struct mbigen_chip *chip,
+					unsigned int nid)
+{
+	struct mbigen_node *tmp, *mbigen;
+	bool found = false;
+
+	if (nid > MG_NR) {
+		pr_warn("MBIGEN: Device ID exceeds max number!!\n");
+		return NULL;
+	}
+
+	list_for_each_entry(mbigen, &chip->nodes, entry) {
+		if (mbigen->nid == nid) {
+			found = true;
+			return mbigen;
+		}
+	}
+
+	/*
+	 * Stop working if no memory available, even if we could
+	 * get what we want.
+	 */
+	tmp = kzalloc(sizeof(*tmp), GFP_KERNEL);
+	if (!tmp)
+		return NULL;
+
+	raw_spin_lock(&chip->lock);
+
+	tmp->chip = chip;
+	tmp->nid = nid;
+	raw_spin_lock_init(&tmp->lock);
+	INIT_LIST_HEAD(&tmp->entry);
+	INIT_LIST_HEAD(&tmp->nodes);
+
+	list_add(&tmp->entry, &chip->nodes);
+	mbigen = tmp;
+	raw_spin_unlock(&chip->lock);
+
+	return mbigen;
+}
+
+/**
+ * get_mbigen_node_type: get the mbigen node type
+ * @nid: the mbigen node value
+ * return 0: evnent id of interrupt connected to this node can be changed.
+ * return 1: evnent id of interrupt connected to this node cant be changed.
+ */
+static int get_mbigen_node_type(int nid)
+{
+	if (nid > MG_NR) {
+		pr_warn("MBIGEN: Device ID exceeds max number!\n");
+		return 1;
+	}
+	if ((nid == 0) || (nid == 5) || (nid > 7))
+		return 0;
+	else
+		return 1;
+}
+
+static int mbigen_write_msg(struct irq_data *d, struct msi_msg *msg)
+{
+	struct mbigen_chip *chip = d->domain->host_data;
+	void __iomem *addr;
+	u32 nid, val, offset;
+	int ret = 0;
+
+	nid = GET_NODE_NUM(d->hwirq);
+	ret = get_mbigen_node_type(nid);
+	if (ret)
+		return 0;
+
+	offset = HWIRQ_OFFSET(d->hwirq);
+
+	addr = chip->base + MBIGEN_VEC_REG_ADDR(nid, offset);
+
+	val = readl_relaxed(addr);
+
+	val &= ~IRQ_EVENT_ID_MASK;
+	val |= (msg->data << IRQ_EVENT_ID_SHIFT);
+
+	writel_relaxed(val, addr);
+	return ret;
+}
+
+/*
+ * Interrupt controller operations
+ */
+static int mbigen_set_type(struct irq_data *d, unsigned int type)
+{
+	struct mbigen_chip *chip = d->domain->host_data;
+	u32 ofst, mask;
+	u32 val, nid, hwirq;
+	void __iomem *addr;
+
+	if (type != IRQ_TYPE_LEVEL_HIGH && type != IRQ_TYPE_EDGE_RISING)
+		return -EINVAL;
+
+	nid = GET_NODE_NUM(d->hwirq);
+	hwirq = HWIRQ_OFFSET(d->hwirq);
+
+	ofst = hwirq / 32 * 4;
+	mask = 1 << (hwirq % 32);
+
+	addr = chip->base + MBIGEN_TYPE_REG_ADDR(nid, ofst);
+	raw_spin_lock(&chip->lock);
+	val = readl_relaxed(addr);
+
+	if (type == IRQ_TYPE_LEVEL_HIGH)
+		val |= mask;
+	else if (type == IRQ_TYPE_EDGE_RISING)
+		val &= ~mask;
+
+	writel_relaxed(val, addr);
+	raw_spin_unlock(&chip->lock);
+
+	return 0;
+}
+
+static void mbigen_mask_irq(struct irq_data *data)
+{
+	irq_chip_mask_parent(data);
+}
+
+static void mbigen_unmask_irq(struct irq_data *data)
+{
+	irq_chip_unmask_parent(data);
+}
+
+static int mbigen_set_affinity(struct irq_data *data,
+			       const struct cpumask *mask,
+			       bool force)
+{
+	int ret;
+
+	ret = irq_chip_set_affinity_parent(data, mask, force);
+	return ret;
+}
+
+static void mbigen_irq_eoi(struct irq_data *d)
+{
+	irq_chip_eoi_parent(d);
+}
+
+static struct irq_chip mbigen_irq_chip = {
+	.name			= "MBIGEN-v2",
+	.irq_mask		= mbigen_mask_irq,
+	.irq_unmask		= mbigen_unmask_irq,
+	.irq_eoi		= mbigen_irq_eoi,
+	.irq_set_affinity	= mbigen_set_affinity,
+	.irq_set_type		= mbigen_set_type,
+};
+
+/*
+ * Interrupt domain operations
+ */
+
+static int mbigen_domain_xlate(struct irq_domain *d,
+			       struct device_node *controller,
+			       const u32 *intspec, unsigned int intsize,
+			       unsigned long *out_hwirq,
+			       unsigned int *out_type)
+{
+
+	if (d->of_node != controller)
+		return -EINVAL;
+
+	if (intsize < 4)
+		return -EINVAL;
+
+	*out_hwirq = COMPOSE_HWIRQ(intspec[3], intspec[2]);
+
+	*out_type = intspec[4] & IRQ_TYPE_SENSE_MASK;
+
+	return 0;
+}
+
+static int mbigen_domain_alloc(struct irq_domain *domain, unsigned int virq,
+			       unsigned int nr_irqs, void *arg)
+{
+	struct mbigen_chip *chip = domain->host_data;
+	struct of_phandle_args *irq_data = arg;
+	irq_hw_number_t hwirq;
+	u32 nid, dev_id, mbi_lines;
+	struct mbigen_node *mgn_node;
+	struct mbigen_device *mgn_dev;
+	msi_alloc_info_t out_arg;
+	int ret = 0, i;
+
+	/* OF style allocation, one interrupt at a time */
+	WARN_ON(nr_irqs != 1);
+
+	dev_id = irq_data->args[0];
+	nid = irq_data->args[3];
+	hwirq = COMPOSE_HWIRQ(nid, irq_data->args[2]);
+
+	mgn_node = get_mbigen_node(chip, nid);
+	if (!mgn_node)
+		return -ENODEV;
+
+	mgn_dev = mbigen_create_device(mgn_node, irq_data->np, virq, nr_irqs);
+	if (!mgn_dev)
+		return -ENOMEM;
+
+	mbi_lines = irq_data->args[1];
+
+	ret = its_msi_prepare(domain, dev_id, mbi_lines, &out_arg);
+	if (ret)
+		return ret;
+
+	ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, &out_arg);
+	if (ret < 0)
+		return ret;
+
+	for (i = 0; i < nr_irqs; i++) {
+		irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i,
+						&mbigen_irq_chip, mgn_dev);
+	}
+
+	return ret;
+}
+
+static void mbigen_domain_free(struct irq_domain *domain, unsigned int virq,
+			       unsigned int nr_irqs)
+{
+	struct irq_data *d = irq_domain_get_irq_data(domain, virq);
+	struct mbigen_device *mgn_dev = irq_data_get_irq_chip_data(d);
+
+	WARN_ON(virq != mgn_dev->irq);
+	WARN_ON(nr_irqs != mgn_dev->nr_irqs);
+	mbigen_free_dev(mgn_dev);
+	irq_domain_free_irqs_parent(domain, virq, nr_irqs);
+}
+
+static void mbigen_domain_activate(struct irq_domain *domain,
+				struct irq_data *irq_data)
+{
+	struct msi_msg msg;
+
+	BUG_ON(irq_chip_compose_msi_msg(irq_data, &msg));
+	mbigen_write_msg(irq_data, &msg);
+}
+
+static void mbigen_domain_deactivate(struct irq_domain *domain,
+				  struct irq_data *irq_data)
+{
+	struct msi_msg msg;
+
+	memset(&msg, 0, sizeof(msg));
+	mbigen_write_msg(irq_data, &msg);
+}
+
+static struct irq_domain_ops mbigen_domain_ops = {
+	.xlate		= mbigen_domain_xlate,
+	.alloc		= mbigen_domain_alloc,
+	.free		= mbigen_domain_free,
+	.activate	= mbigen_domain_activate,
+	.deactivate	= mbigen_domain_deactivate,
+};
+
+/*
+ * Early initialization as an interrupt controller
+ */
+static int __init mbigen_of_init(struct device_node *node,
+				 struct device_node *parent_node)
+{
+	struct mbigen_chip *chip;
+	struct irq_domain *parent_domain;
+	int err;
+
+	parent_node = of_parse_phandle(node, "msi-parent", 0);
+
+	if (!parent_node) {
+		pr_warn("MBIGEN: no ITS node for %s\n", node->full_name);
+		return -ENXIO;
+	}
+
+	parent_domain = get_its_domain(parent_node);
+
+	if (!parent_domain) {
+		pr_warn("MBIGEN: no ITS domain for %s\n", node->full_name);
+		return -ENXIO;
+	}
+
+	chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+	if (!chip)
+		return -ENOMEM;
+
+	chip->base = of_iomap(node, 0);
+	if (!chip->base) {
+		pr_err("%s: Registers not found.\n", node->full_name);
+		err = -ENXIO;
+		goto free_chip;
+	}
+
+	chip->domain = irq_domain_add_hierarchy(parent_domain,
+						0, MG_NR_IRQS, node,
+						&mbigen_domain_ops, chip);
+
+	if (!chip->domain) {
+		err = -ENOMEM;
+		goto unmap_reg;
+	}
+
+	chip->node = node;
+	raw_spin_lock_init(&chip->lock);
+	INIT_LIST_HEAD(&chip->entry);
+	INIT_LIST_HEAD(&chip->nodes);
+	pr_debug("MBIGEN: %s\n", node->full_name);
+	spin_lock(&mbigen_lock);
+	list_add(&chip->entry, &mbigen_chip_list);
+	spin_unlock(&mbigen_lock);
+
+	return 0;
+
+unmap_reg:
+	iounmap(chip->base);
+free_chip:
+	kfree(chip);
+	pr_warn("MBIGEN: failed probing %s\n", node->full_name);
+	return err;
+}
+IRQCHIP_DECLARE(hisi_mbigen, "hisilicon,mbigen-v2", mbigen_of_init);
+
+MODULE_AUTHOR("Jun Ma <majun258@huawei.com>");
+MODULE_AUTHOR("Yun Wu <wuyun.wu@huawei.com>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Hisilicon MBI Generator driver");
diff --git a/include/linux/mbi.h b/include/linux/mbi.h
new file mode 100644
index 0000000..d3b8155
--- /dev/null
+++ b/include/linux/mbi.h
@@ -0,0 +1,15 @@ 
+#ifndef _LINUX_MBI_H
+#define _LINUX_MBI_H
+
+#include <linux/device.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/msi.h>
+
+
+/* Function to parse and map message interrupts */
+extern int its_msi_prepare(struct irq_domain *domain, u32 dev_id,
+		    int nvec, msi_alloc_info_t *info);
+extern struct irq_domain *get_its_domain(struct device_node *node);
+
+#endif /* _LINUX_MBI_H */