All of lore.kernel.org
 help / color / mirror / Atom feed
From: Jianjun Wang <jianjun.wang@mediatek.com>
To: Marc Zyngier <maz@kernel.org>
Cc: <youlin.pei@mediatek.com>, <devicetree@vger.kernel.org>,
	Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>,
	<Rex-BC.Chen@mediatek.com>, <qizhong.cheng@mediatek.com>,
	<chuanjia.liu@mediatek.com>, <linux-pci@vger.kernel.org>,
	<drinkcat@chromium.org>, Ryder Lee <ryder.lee@mediatek.com>,
	<anson.chuang@mediatek.com>, <linux-kernel@vger.kernel.org>,
	Matthias Brugger <matthias.bgg@gmail.com>,
	"Sj Huang" <sj.huang@mediatek.com>,
	Rob Herring <robh+dt@kernel.org>,
	<linux-mediatek@lists.infradead.org>,
	Philipp Zabel <p.zabel@pengutronix.de>,
	Bjorn Helgaas <bhelgaas@google.com>, <sin_jieyang@mediatek.com>,
	<linux-arm-kernel@lists.infradead.org>
Subject: Re: [v7,4/7] PCI: mediatek-gen3: Add INTx support
Date: Wed, 27 Jan 2021 21:09:14 +0800	[thread overview]
Message-ID: <1611752954.14672.71.camel@mhfsdcap03> (raw)
In-Reply-To: <123724a33c29d1f078f2a65795c6e208@kernel.org>

On Tue, 2021-01-26 at 12:25 +0000, Marc Zyngier wrote:
> On 2021-01-13 11:39, Jianjun Wang wrote:
> > Add INTx support for MediaTek Gen3 PCIe controller.
> > 
> > Signed-off-by: Jianjun Wang <jianjun.wang@mediatek.com>
> > Acked-by: Ryder Lee <ryder.lee@mediatek.com>
> > ---
> >  drivers/pci/controller/pcie-mediatek-gen3.c | 163 ++++++++++++++++++++
> >  1 file changed, 163 insertions(+)
> > 
> > diff --git a/drivers/pci/controller/pcie-mediatek-gen3.c
> > b/drivers/pci/controller/pcie-mediatek-gen3.c
> > index c00ea7c167de..7979a2856c35 100644
> > --- a/drivers/pci/controller/pcie-mediatek-gen3.c
> > +++ b/drivers/pci/controller/pcie-mediatek-gen3.c
> > @@ -9,6 +9,9 @@
> >  #include <linux/clk.h>
> >  #include <linux/delay.h>
> >  #include <linux/iopoll.h>
> > +#include <linux/irq.h>
> > +#include <linux/irqchip/chained_irq.h>
> > +#include <linux/irqdomain.h>
> >  #include <linux/kernel.h>
> >  #include <linux/module.h>
> >  #include <linux/of_address.h>
> > @@ -49,6 +52,12 @@
> >  #define PCIE_LINK_STATUS_REG		0x154
> >  #define PCIE_PORT_LINKUP		BIT(8)
> > 
> > +#define PCIE_INT_ENABLE_REG		0x180
> > +#define PCIE_INTX_SHIFT			24
> > +#define PCIE_INTX_MASK			GENMASK(27, 24)
> 
> I guess this '24' is actually PCIE_INTX_SHIFT? In this case,
> please write it as
> 
> GENMASK(PCIE_INTX_SHIFT + PCI_NUM_INTX - 1, PCIE_INTX_SHIFT)
> 
> to make it clear that you are dealing with one bit per INTx.

Yes, I will fix it in the next version, thanks for your review.
> 
> > +
> > +#define PCIE_INT_STATUS_REG		0x184
> > +
> >  #define PCIE_TRANS_TABLE_BASE_REG	0x800
> >  #define PCIE_ATR_SRC_ADDR_MSB_OFFSET	0x4
> >  #define PCIE_ATR_TRSL_ADDR_LSB_OFFSET	0x8
> > @@ -77,6 +86,8 @@
> >   * @phy: PHY controller block
> >   * @clks: PCIe clocks
> >   * @num_clks: PCIe clocks count for this port
> > + * @irq: PCIe controller interrupt number
> > + * @intx_domain: legacy INTx IRQ domain
> >   */
> >  struct mtk_pcie_port {
> >  	struct device *dev;
> > @@ -87,6 +98,9 @@ struct mtk_pcie_port {
> >  	struct phy *phy;
> >  	struct clk_bulk_data *clks;
> >  	int num_clks;
> > +
> > +	int irq;
> > +	struct irq_domain *intx_domain;
> >  };
> > 
> >  /**
> > @@ -266,6 +280,149 @@ static int mtk_pcie_startup_port(struct
> > mtk_pcie_port *port)
> >  	return 0;
> >  }
> > 
> > +static int mtk_pcie_set_affinity(struct irq_data *data,
> > +				 const struct cpumask *mask, bool force)
> > +{
> > +	return -EINVAL;
> > +}
> > +
> > +static void mtk_intx_mask(struct irq_data *data)
> > +{
> > +	struct mtk_pcie_port *port = irq_data_get_irq_chip_data(data);
> > +	u32 val;
> > +
> > +	val = readl_relaxed(port->base + PCIE_INT_ENABLE_REG);
> > +	val &= ~BIT(data->hwirq + PCIE_INTX_SHIFT);
> > +	writel_relaxed(val, port->base + PCIE_INT_ENABLE_REG);
> 
> This is missing some locking. Otherwise, two concurrent mask/unmask
> for different interrupts will corrupt each other's state.
> 
> > +}
> > +
> > +static void mtk_intx_unmask(struct irq_data *data)
> > +{
> > +	struct mtk_pcie_port *port = irq_data_get_irq_chip_data(data);
> > +	u32 val;
> > +
> > +	val = readl_relaxed(port->base + PCIE_INT_ENABLE_REG);
> > +	val |= BIT(data->hwirq + PCIE_INTX_SHIFT);
> > +	writel_relaxed(val, port->base + PCIE_INT_ENABLE_REG);
> 
> Same thing here.
> 
> > +}
> > +
> > +/**
> > + * mtk_intx_eoi
> > + * @data: pointer to chip specific data
> > + *
> > + * As an emulated level IRQ, its interrupt status will remain
> > + * until the corresponding de-assert message is received; hence that
> > + * the status can only be cleared when the interrupt has been 
> > serviced.
> > + */
> > +static void mtk_intx_eoi(struct irq_data *data)
> > +{
> > +	struct mtk_pcie_port *port = irq_data_get_irq_chip_data(data);
> > +	unsigned long hwirq;
> > +
> > +	hwirq = data->hwirq + PCIE_INTX_SHIFT;
> > +	writel_relaxed(BIT(hwirq), port->base + PCIE_INT_STATUS_REG);
> > +}
> > +
> > +static struct irq_chip mtk_intx_irq_chip = {
> > +	.irq_mask		= mtk_intx_mask,
> > +	.irq_unmask		= mtk_intx_unmask,
> > +	.irq_eoi		= mtk_intx_eoi,
> > +	.irq_set_affinity	= mtk_pcie_set_affinity,
> > +	.name			= "PCIe",
> 
> nit: "PCIe" is not really descriptive. "INTx" would be a bit better.
> 
> > +};
> > +
> > +static int mtk_pcie_intx_map(struct irq_domain *domain, unsigned int 
> > irq,
> > +			     irq_hw_number_t hwirq)
> > +{
> > +	irq_set_chip_and_handler_name(irq, &mtk_intx_irq_chip,
> > +				      handle_fasteoi_irq, "INTx");
> > +	irq_set_chip_data(irq, domain->host_data);
> 
> You probably want to set the chip_data *before* wiring
> the handler, as otherwise you could end-up with a NULL
> pointer in any of the callbacks if the interrupt fires
> between the two.
> 
> > +
> > +	return 0;
> > +}
> > +
> > +static const struct irq_domain_ops intx_domain_ops = {
> > +	.map = mtk_pcie_intx_map,
> > +};
> > +
> > +static int mtk_pcie_init_irq_domains(struct mtk_pcie_port *port,
> > +				     struct device_node *node)
> > +{
> > +	struct device *dev = port->dev;
> > +	struct device_node *intc_node;
> > +
> > +	/* Setup INTx */
> > +	intc_node = of_get_child_by_name(node, "interrupt-controller");
> > +	if (!intc_node) {
> > +		dev_err(dev, "missing PCIe Intc node\n");
> > +		return -ENODEV;
> > +	}
> > +
> > +	port->intx_domain = irq_domain_add_linear(intc_node, PCI_NUM_INTX,
> > +						  &intx_domain_ops, port);
> > +	if (!port->intx_domain) {
> > +		dev_err(dev, "failed to get INTx IRQ domain\n");
> > +		return -ENODEV;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static void mtk_pcie_irq_teardown(struct mtk_pcie_port *port)
> > +{
> > +	irq_set_chained_handler_and_data(port->irq, NULL, NULL);
> > +
> > +	if (port->intx_domain)
> > +		irq_domain_remove(port->intx_domain);
> > +
> > +	irq_dispose_mapping(port->irq);
> > +}
> > +
> > +static void mtk_pcie_irq_handler(struct irq_desc *desc)
> > +{
> > +	struct mtk_pcie_port *port = irq_desc_get_handler_data(desc);
> > +	struct irq_chip *irqchip = irq_desc_get_chip(desc);
> > +	unsigned long status;
> > +	unsigned int virq;
> > +	irq_hw_number_t irq_bit = PCIE_INTX_SHIFT;
> > +
> > +	chained_irq_enter(irqchip, desc);
> > +
> > +	status = readl_relaxed(port->base + PCIE_INT_STATUS_REG);
> > +	if (status & PCIE_INTX_MASK) {
> 
> This "if (status & PCIE_INTX_MASK)" is already implicit from
> the for_each_set_bit_from() iterator, and you can drop it.
> 
> > +		for_each_set_bit_from(irq_bit, &status, PCI_NUM_INTX +
> > +				      PCIE_INTX_SHIFT) {
> > +			virq = irq_find_mapping(port->intx_domain,
> > +						irq_bit - PCIE_INTX_SHIFT);
> > +			generic_handle_irq(virq);
> > +		}
> > +	}
> > +
> > +	chained_irq_exit(irqchip, desc);
> > +}
> > +
> > +static int mtk_pcie_setup_irq(struct mtk_pcie_port *port,
> > +			      struct device_node *node)
> > +{
> > +	struct device *dev = port->dev;
> > +	struct platform_device *pdev = to_platform_device(dev);
> > +	int err;
> > +
> > +	err = mtk_pcie_init_irq_domains(port, node);
> > +	if (err) {
> > +		dev_err(dev, "failed to init PCIe IRQ domain\n");
> > +		return err;
> > +	}
> > +
> > +	port->irq = platform_get_irq(pdev, 0);
> > +	if (port->irq < 0)
> > +		return port->irq;
> > +
> > +	irq_set_chained_handler_and_data(port->irq, mtk_pcie_irq_handler, 
> > port);
> 
> You seem to be missing something that will mask all INTx interrupts
> as an initial state.

Yes, the INTx default state of this chip is disabled, but there are
still risks for driver use, I will add mask all INTx interrupts to
mtk_pcie_startup_port function in the next version.

Thanks.
> 
> > +
> > +	return 0;
> > +}
> > +
> >  static int mtk_pcie_clk_init(struct mtk_pcie_port *port)
> >  {
> >  	int ret;
> > @@ -388,6 +545,10 @@ static int mtk_pcie_setup(struct mtk_pcie_port 
> > *port)
> >  		goto err_setup;
> >  	}
> > 
> > +	err = mtk_pcie_setup_irq(port, dev->of_node);
> > +	if (err)
> > +		goto err_setup;
> > +
> >  	dev_info(dev, "PCIe link up success!\n");
> > 
> >  	return 0;
> > @@ -423,6 +584,7 @@ static int mtk_pcie_probe(struct platform_device 
> > *pdev)
> > 
> >  	err = pci_host_probe(host);
> >  	if (err) {
> > +		mtk_pcie_irq_teardown(port);
> >  		mtk_pcie_power_down(port);
> >  		return err;
> >  	}
> > @@ -440,6 +602,7 @@ static int mtk_pcie_remove(struct platform_device 
> > *pdev)
> >  	pci_remove_root_bus(host->bus);
> >  	pci_unlock_rescan_remove();
> > 
> > +	mtk_pcie_irq_teardown(port);
> >  	mtk_pcie_power_down(port);
> > 
> >  	return 0;
> 
> Thanks,
> 
>          M.


WARNING: multiple messages have this Message-ID (diff)
From: Jianjun Wang <jianjun.wang@mediatek.com>
To: Marc Zyngier <maz@kernel.org>
Cc: youlin.pei@mediatek.com, devicetree@vger.kernel.org,
	Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>,
	qizhong.cheng@mediatek.com, chuanjia.liu@mediatek.com,
	linux-pci@vger.kernel.org, Ryder Lee <ryder.lee@mediatek.com>,
	Rob Herring <robh+dt@kernel.org>,
	anson.chuang@mediatek.com,
	Matthias Brugger <matthias.bgg@gmail.com>,
	Sj Huang <sj.huang@mediatek.com>,
	drinkcat@chromium.org, linux-mediatek@lists.infradead.org,
	linux-arm-kernel@lists.infradead.org,
	Philipp Zabel <p.zabel@pengutronix.de>,
	Rex-BC.Chen@mediatek.com, sin_jieyang@mediatek.com,
	Bjorn Helgaas <bhelgaas@google.com>,
	linux-kernel@vger.kernel.org
Subject: Re: [v7,4/7] PCI: mediatek-gen3: Add INTx support
Date: Wed, 27 Jan 2021 21:09:14 +0800	[thread overview]
Message-ID: <1611752954.14672.71.camel@mhfsdcap03> (raw)
In-Reply-To: <123724a33c29d1f078f2a65795c6e208@kernel.org>

On Tue, 2021-01-26 at 12:25 +0000, Marc Zyngier wrote:
> On 2021-01-13 11:39, Jianjun Wang wrote:
> > Add INTx support for MediaTek Gen3 PCIe controller.
> > 
> > Signed-off-by: Jianjun Wang <jianjun.wang@mediatek.com>
> > Acked-by: Ryder Lee <ryder.lee@mediatek.com>
> > ---
> >  drivers/pci/controller/pcie-mediatek-gen3.c | 163 ++++++++++++++++++++
> >  1 file changed, 163 insertions(+)
> > 
> > diff --git a/drivers/pci/controller/pcie-mediatek-gen3.c
> > b/drivers/pci/controller/pcie-mediatek-gen3.c
> > index c00ea7c167de..7979a2856c35 100644
> > --- a/drivers/pci/controller/pcie-mediatek-gen3.c
> > +++ b/drivers/pci/controller/pcie-mediatek-gen3.c
> > @@ -9,6 +9,9 @@
> >  #include <linux/clk.h>
> >  #include <linux/delay.h>
> >  #include <linux/iopoll.h>
> > +#include <linux/irq.h>
> > +#include <linux/irqchip/chained_irq.h>
> > +#include <linux/irqdomain.h>
> >  #include <linux/kernel.h>
> >  #include <linux/module.h>
> >  #include <linux/of_address.h>
> > @@ -49,6 +52,12 @@
> >  #define PCIE_LINK_STATUS_REG		0x154
> >  #define PCIE_PORT_LINKUP		BIT(8)
> > 
> > +#define PCIE_INT_ENABLE_REG		0x180
> > +#define PCIE_INTX_SHIFT			24
> > +#define PCIE_INTX_MASK			GENMASK(27, 24)
> 
> I guess this '24' is actually PCIE_INTX_SHIFT? In this case,
> please write it as
> 
> GENMASK(PCIE_INTX_SHIFT + PCI_NUM_INTX - 1, PCIE_INTX_SHIFT)
> 
> to make it clear that you are dealing with one bit per INTx.

Yes, I will fix it in the next version, thanks for your review.
> 
> > +
> > +#define PCIE_INT_STATUS_REG		0x184
> > +
> >  #define PCIE_TRANS_TABLE_BASE_REG	0x800
> >  #define PCIE_ATR_SRC_ADDR_MSB_OFFSET	0x4
> >  #define PCIE_ATR_TRSL_ADDR_LSB_OFFSET	0x8
> > @@ -77,6 +86,8 @@
> >   * @phy: PHY controller block
> >   * @clks: PCIe clocks
> >   * @num_clks: PCIe clocks count for this port
> > + * @irq: PCIe controller interrupt number
> > + * @intx_domain: legacy INTx IRQ domain
> >   */
> >  struct mtk_pcie_port {
> >  	struct device *dev;
> > @@ -87,6 +98,9 @@ struct mtk_pcie_port {
> >  	struct phy *phy;
> >  	struct clk_bulk_data *clks;
> >  	int num_clks;
> > +
> > +	int irq;
> > +	struct irq_domain *intx_domain;
> >  };
> > 
> >  /**
> > @@ -266,6 +280,149 @@ static int mtk_pcie_startup_port(struct
> > mtk_pcie_port *port)
> >  	return 0;
> >  }
> > 
> > +static int mtk_pcie_set_affinity(struct irq_data *data,
> > +				 const struct cpumask *mask, bool force)
> > +{
> > +	return -EINVAL;
> > +}
> > +
> > +static void mtk_intx_mask(struct irq_data *data)
> > +{
> > +	struct mtk_pcie_port *port = irq_data_get_irq_chip_data(data);
> > +	u32 val;
> > +
> > +	val = readl_relaxed(port->base + PCIE_INT_ENABLE_REG);
> > +	val &= ~BIT(data->hwirq + PCIE_INTX_SHIFT);
> > +	writel_relaxed(val, port->base + PCIE_INT_ENABLE_REG);
> 
> This is missing some locking. Otherwise, two concurrent mask/unmask
> for different interrupts will corrupt each other's state.
> 
> > +}
> > +
> > +static void mtk_intx_unmask(struct irq_data *data)
> > +{
> > +	struct mtk_pcie_port *port = irq_data_get_irq_chip_data(data);
> > +	u32 val;
> > +
> > +	val = readl_relaxed(port->base + PCIE_INT_ENABLE_REG);
> > +	val |= BIT(data->hwirq + PCIE_INTX_SHIFT);
> > +	writel_relaxed(val, port->base + PCIE_INT_ENABLE_REG);
> 
> Same thing here.
> 
> > +}
> > +
> > +/**
> > + * mtk_intx_eoi
> > + * @data: pointer to chip specific data
> > + *
> > + * As an emulated level IRQ, its interrupt status will remain
> > + * until the corresponding de-assert message is received; hence that
> > + * the status can only be cleared when the interrupt has been 
> > serviced.
> > + */
> > +static void mtk_intx_eoi(struct irq_data *data)
> > +{
> > +	struct mtk_pcie_port *port = irq_data_get_irq_chip_data(data);
> > +	unsigned long hwirq;
> > +
> > +	hwirq = data->hwirq + PCIE_INTX_SHIFT;
> > +	writel_relaxed(BIT(hwirq), port->base + PCIE_INT_STATUS_REG);
> > +}
> > +
> > +static struct irq_chip mtk_intx_irq_chip = {
> > +	.irq_mask		= mtk_intx_mask,
> > +	.irq_unmask		= mtk_intx_unmask,
> > +	.irq_eoi		= mtk_intx_eoi,
> > +	.irq_set_affinity	= mtk_pcie_set_affinity,
> > +	.name			= "PCIe",
> 
> nit: "PCIe" is not really descriptive. "INTx" would be a bit better.
> 
> > +};
> > +
> > +static int mtk_pcie_intx_map(struct irq_domain *domain, unsigned int 
> > irq,
> > +			     irq_hw_number_t hwirq)
> > +{
> > +	irq_set_chip_and_handler_name(irq, &mtk_intx_irq_chip,
> > +				      handle_fasteoi_irq, "INTx");
> > +	irq_set_chip_data(irq, domain->host_data);
> 
> You probably want to set the chip_data *before* wiring
> the handler, as otherwise you could end-up with a NULL
> pointer in any of the callbacks if the interrupt fires
> between the two.
> 
> > +
> > +	return 0;
> > +}
> > +
> > +static const struct irq_domain_ops intx_domain_ops = {
> > +	.map = mtk_pcie_intx_map,
> > +};
> > +
> > +static int mtk_pcie_init_irq_domains(struct mtk_pcie_port *port,
> > +				     struct device_node *node)
> > +{
> > +	struct device *dev = port->dev;
> > +	struct device_node *intc_node;
> > +
> > +	/* Setup INTx */
> > +	intc_node = of_get_child_by_name(node, "interrupt-controller");
> > +	if (!intc_node) {
> > +		dev_err(dev, "missing PCIe Intc node\n");
> > +		return -ENODEV;
> > +	}
> > +
> > +	port->intx_domain = irq_domain_add_linear(intc_node, PCI_NUM_INTX,
> > +						  &intx_domain_ops, port);
> > +	if (!port->intx_domain) {
> > +		dev_err(dev, "failed to get INTx IRQ domain\n");
> > +		return -ENODEV;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static void mtk_pcie_irq_teardown(struct mtk_pcie_port *port)
> > +{
> > +	irq_set_chained_handler_and_data(port->irq, NULL, NULL);
> > +
> > +	if (port->intx_domain)
> > +		irq_domain_remove(port->intx_domain);
> > +
> > +	irq_dispose_mapping(port->irq);
> > +}
> > +
> > +static void mtk_pcie_irq_handler(struct irq_desc *desc)
> > +{
> > +	struct mtk_pcie_port *port = irq_desc_get_handler_data(desc);
> > +	struct irq_chip *irqchip = irq_desc_get_chip(desc);
> > +	unsigned long status;
> > +	unsigned int virq;
> > +	irq_hw_number_t irq_bit = PCIE_INTX_SHIFT;
> > +
> > +	chained_irq_enter(irqchip, desc);
> > +
> > +	status = readl_relaxed(port->base + PCIE_INT_STATUS_REG);
> > +	if (status & PCIE_INTX_MASK) {
> 
> This "if (status & PCIE_INTX_MASK)" is already implicit from
> the for_each_set_bit_from() iterator, and you can drop it.
> 
> > +		for_each_set_bit_from(irq_bit, &status, PCI_NUM_INTX +
> > +				      PCIE_INTX_SHIFT) {
> > +			virq = irq_find_mapping(port->intx_domain,
> > +						irq_bit - PCIE_INTX_SHIFT);
> > +			generic_handle_irq(virq);
> > +		}
> > +	}
> > +
> > +	chained_irq_exit(irqchip, desc);
> > +}
> > +
> > +static int mtk_pcie_setup_irq(struct mtk_pcie_port *port,
> > +			      struct device_node *node)
> > +{
> > +	struct device *dev = port->dev;
> > +	struct platform_device *pdev = to_platform_device(dev);
> > +	int err;
> > +
> > +	err = mtk_pcie_init_irq_domains(port, node);
> > +	if (err) {
> > +		dev_err(dev, "failed to init PCIe IRQ domain\n");
> > +		return err;
> > +	}
> > +
> > +	port->irq = platform_get_irq(pdev, 0);
> > +	if (port->irq < 0)
> > +		return port->irq;
> > +
> > +	irq_set_chained_handler_and_data(port->irq, mtk_pcie_irq_handler, 
> > port);
> 
> You seem to be missing something that will mask all INTx interrupts
> as an initial state.

Yes, the INTx default state of this chip is disabled, but there are
still risks for driver use, I will add mask all INTx interrupts to
mtk_pcie_startup_port function in the next version.

Thanks.
> 
> > +
> > +	return 0;
> > +}
> > +
> >  static int mtk_pcie_clk_init(struct mtk_pcie_port *port)
> >  {
> >  	int ret;
> > @@ -388,6 +545,10 @@ static int mtk_pcie_setup(struct mtk_pcie_port 
> > *port)
> >  		goto err_setup;
> >  	}
> > 
> > +	err = mtk_pcie_setup_irq(port, dev->of_node);
> > +	if (err)
> > +		goto err_setup;
> > +
> >  	dev_info(dev, "PCIe link up success!\n");
> > 
> >  	return 0;
> > @@ -423,6 +584,7 @@ static int mtk_pcie_probe(struct platform_device 
> > *pdev)
> > 
> >  	err = pci_host_probe(host);
> >  	if (err) {
> > +		mtk_pcie_irq_teardown(port);
> >  		mtk_pcie_power_down(port);
> >  		return err;
> >  	}
> > @@ -440,6 +602,7 @@ static int mtk_pcie_remove(struct platform_device 
> > *pdev)
> >  	pci_remove_root_bus(host->bus);
> >  	pci_unlock_rescan_remove();
> > 
> > +	mtk_pcie_irq_teardown(port);
> >  	mtk_pcie_power_down(port);
> > 
> >  	return 0;
> 
> Thanks,
> 
>          M.

_______________________________________________
Linux-mediatek mailing list
Linux-mediatek@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-mediatek

WARNING: multiple messages have this Message-ID (diff)
From: Jianjun Wang <jianjun.wang@mediatek.com>
To: Marc Zyngier <maz@kernel.org>
Cc: youlin.pei@mediatek.com, devicetree@vger.kernel.org,
	Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>,
	qizhong.cheng@mediatek.com, chuanjia.liu@mediatek.com,
	linux-pci@vger.kernel.org, Ryder Lee <ryder.lee@mediatek.com>,
	Rob Herring <robh+dt@kernel.org>,
	anson.chuang@mediatek.com,
	Matthias Brugger <matthias.bgg@gmail.com>,
	Sj Huang <sj.huang@mediatek.com>,
	drinkcat@chromium.org, linux-mediatek@lists.infradead.org,
	linux-arm-kernel@lists.infradead.org,
	Philipp Zabel <p.zabel@pengutronix.de>,
	Rex-BC.Chen@mediatek.com, sin_jieyang@mediatek.com,
	Bjorn Helgaas <bhelgaas@google.com>,
	linux-kernel@vger.kernel.org
Subject: Re: [v7,4/7] PCI: mediatek-gen3: Add INTx support
Date: Wed, 27 Jan 2021 21:09:14 +0800	[thread overview]
Message-ID: <1611752954.14672.71.camel@mhfsdcap03> (raw)
In-Reply-To: <123724a33c29d1f078f2a65795c6e208@kernel.org>

On Tue, 2021-01-26 at 12:25 +0000, Marc Zyngier wrote:
> On 2021-01-13 11:39, Jianjun Wang wrote:
> > Add INTx support for MediaTek Gen3 PCIe controller.
> > 
> > Signed-off-by: Jianjun Wang <jianjun.wang@mediatek.com>
> > Acked-by: Ryder Lee <ryder.lee@mediatek.com>
> > ---
> >  drivers/pci/controller/pcie-mediatek-gen3.c | 163 ++++++++++++++++++++
> >  1 file changed, 163 insertions(+)
> > 
> > diff --git a/drivers/pci/controller/pcie-mediatek-gen3.c
> > b/drivers/pci/controller/pcie-mediatek-gen3.c
> > index c00ea7c167de..7979a2856c35 100644
> > --- a/drivers/pci/controller/pcie-mediatek-gen3.c
> > +++ b/drivers/pci/controller/pcie-mediatek-gen3.c
> > @@ -9,6 +9,9 @@
> >  #include <linux/clk.h>
> >  #include <linux/delay.h>
> >  #include <linux/iopoll.h>
> > +#include <linux/irq.h>
> > +#include <linux/irqchip/chained_irq.h>
> > +#include <linux/irqdomain.h>
> >  #include <linux/kernel.h>
> >  #include <linux/module.h>
> >  #include <linux/of_address.h>
> > @@ -49,6 +52,12 @@
> >  #define PCIE_LINK_STATUS_REG		0x154
> >  #define PCIE_PORT_LINKUP		BIT(8)
> > 
> > +#define PCIE_INT_ENABLE_REG		0x180
> > +#define PCIE_INTX_SHIFT			24
> > +#define PCIE_INTX_MASK			GENMASK(27, 24)
> 
> I guess this '24' is actually PCIE_INTX_SHIFT? In this case,
> please write it as
> 
> GENMASK(PCIE_INTX_SHIFT + PCI_NUM_INTX - 1, PCIE_INTX_SHIFT)
> 
> to make it clear that you are dealing with one bit per INTx.

Yes, I will fix it in the next version, thanks for your review.
> 
> > +
> > +#define PCIE_INT_STATUS_REG		0x184
> > +
> >  #define PCIE_TRANS_TABLE_BASE_REG	0x800
> >  #define PCIE_ATR_SRC_ADDR_MSB_OFFSET	0x4
> >  #define PCIE_ATR_TRSL_ADDR_LSB_OFFSET	0x8
> > @@ -77,6 +86,8 @@
> >   * @phy: PHY controller block
> >   * @clks: PCIe clocks
> >   * @num_clks: PCIe clocks count for this port
> > + * @irq: PCIe controller interrupt number
> > + * @intx_domain: legacy INTx IRQ domain
> >   */
> >  struct mtk_pcie_port {
> >  	struct device *dev;
> > @@ -87,6 +98,9 @@ struct mtk_pcie_port {
> >  	struct phy *phy;
> >  	struct clk_bulk_data *clks;
> >  	int num_clks;
> > +
> > +	int irq;
> > +	struct irq_domain *intx_domain;
> >  };
> > 
> >  /**
> > @@ -266,6 +280,149 @@ static int mtk_pcie_startup_port(struct
> > mtk_pcie_port *port)
> >  	return 0;
> >  }
> > 
> > +static int mtk_pcie_set_affinity(struct irq_data *data,
> > +				 const struct cpumask *mask, bool force)
> > +{
> > +	return -EINVAL;
> > +}
> > +
> > +static void mtk_intx_mask(struct irq_data *data)
> > +{
> > +	struct mtk_pcie_port *port = irq_data_get_irq_chip_data(data);
> > +	u32 val;
> > +
> > +	val = readl_relaxed(port->base + PCIE_INT_ENABLE_REG);
> > +	val &= ~BIT(data->hwirq + PCIE_INTX_SHIFT);
> > +	writel_relaxed(val, port->base + PCIE_INT_ENABLE_REG);
> 
> This is missing some locking. Otherwise, two concurrent mask/unmask
> for different interrupts will corrupt each other's state.
> 
> > +}
> > +
> > +static void mtk_intx_unmask(struct irq_data *data)
> > +{
> > +	struct mtk_pcie_port *port = irq_data_get_irq_chip_data(data);
> > +	u32 val;
> > +
> > +	val = readl_relaxed(port->base + PCIE_INT_ENABLE_REG);
> > +	val |= BIT(data->hwirq + PCIE_INTX_SHIFT);
> > +	writel_relaxed(val, port->base + PCIE_INT_ENABLE_REG);
> 
> Same thing here.
> 
> > +}
> > +
> > +/**
> > + * mtk_intx_eoi
> > + * @data: pointer to chip specific data
> > + *
> > + * As an emulated level IRQ, its interrupt status will remain
> > + * until the corresponding de-assert message is received; hence that
> > + * the status can only be cleared when the interrupt has been 
> > serviced.
> > + */
> > +static void mtk_intx_eoi(struct irq_data *data)
> > +{
> > +	struct mtk_pcie_port *port = irq_data_get_irq_chip_data(data);
> > +	unsigned long hwirq;
> > +
> > +	hwirq = data->hwirq + PCIE_INTX_SHIFT;
> > +	writel_relaxed(BIT(hwirq), port->base + PCIE_INT_STATUS_REG);
> > +}
> > +
> > +static struct irq_chip mtk_intx_irq_chip = {
> > +	.irq_mask		= mtk_intx_mask,
> > +	.irq_unmask		= mtk_intx_unmask,
> > +	.irq_eoi		= mtk_intx_eoi,
> > +	.irq_set_affinity	= mtk_pcie_set_affinity,
> > +	.name			= "PCIe",
> 
> nit: "PCIe" is not really descriptive. "INTx" would be a bit better.
> 
> > +};
> > +
> > +static int mtk_pcie_intx_map(struct irq_domain *domain, unsigned int 
> > irq,
> > +			     irq_hw_number_t hwirq)
> > +{
> > +	irq_set_chip_and_handler_name(irq, &mtk_intx_irq_chip,
> > +				      handle_fasteoi_irq, "INTx");
> > +	irq_set_chip_data(irq, domain->host_data);
> 
> You probably want to set the chip_data *before* wiring
> the handler, as otherwise you could end-up with a NULL
> pointer in any of the callbacks if the interrupt fires
> between the two.
> 
> > +
> > +	return 0;
> > +}
> > +
> > +static const struct irq_domain_ops intx_domain_ops = {
> > +	.map = mtk_pcie_intx_map,
> > +};
> > +
> > +static int mtk_pcie_init_irq_domains(struct mtk_pcie_port *port,
> > +				     struct device_node *node)
> > +{
> > +	struct device *dev = port->dev;
> > +	struct device_node *intc_node;
> > +
> > +	/* Setup INTx */
> > +	intc_node = of_get_child_by_name(node, "interrupt-controller");
> > +	if (!intc_node) {
> > +		dev_err(dev, "missing PCIe Intc node\n");
> > +		return -ENODEV;
> > +	}
> > +
> > +	port->intx_domain = irq_domain_add_linear(intc_node, PCI_NUM_INTX,
> > +						  &intx_domain_ops, port);
> > +	if (!port->intx_domain) {
> > +		dev_err(dev, "failed to get INTx IRQ domain\n");
> > +		return -ENODEV;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static void mtk_pcie_irq_teardown(struct mtk_pcie_port *port)
> > +{
> > +	irq_set_chained_handler_and_data(port->irq, NULL, NULL);
> > +
> > +	if (port->intx_domain)
> > +		irq_domain_remove(port->intx_domain);
> > +
> > +	irq_dispose_mapping(port->irq);
> > +}
> > +
> > +static void mtk_pcie_irq_handler(struct irq_desc *desc)
> > +{
> > +	struct mtk_pcie_port *port = irq_desc_get_handler_data(desc);
> > +	struct irq_chip *irqchip = irq_desc_get_chip(desc);
> > +	unsigned long status;
> > +	unsigned int virq;
> > +	irq_hw_number_t irq_bit = PCIE_INTX_SHIFT;
> > +
> > +	chained_irq_enter(irqchip, desc);
> > +
> > +	status = readl_relaxed(port->base + PCIE_INT_STATUS_REG);
> > +	if (status & PCIE_INTX_MASK) {
> 
> This "if (status & PCIE_INTX_MASK)" is already implicit from
> the for_each_set_bit_from() iterator, and you can drop it.
> 
> > +		for_each_set_bit_from(irq_bit, &status, PCI_NUM_INTX +
> > +				      PCIE_INTX_SHIFT) {
> > +			virq = irq_find_mapping(port->intx_domain,
> > +						irq_bit - PCIE_INTX_SHIFT);
> > +			generic_handle_irq(virq);
> > +		}
> > +	}
> > +
> > +	chained_irq_exit(irqchip, desc);
> > +}
> > +
> > +static int mtk_pcie_setup_irq(struct mtk_pcie_port *port,
> > +			      struct device_node *node)
> > +{
> > +	struct device *dev = port->dev;
> > +	struct platform_device *pdev = to_platform_device(dev);
> > +	int err;
> > +
> > +	err = mtk_pcie_init_irq_domains(port, node);
> > +	if (err) {
> > +		dev_err(dev, "failed to init PCIe IRQ domain\n");
> > +		return err;
> > +	}
> > +
> > +	port->irq = platform_get_irq(pdev, 0);
> > +	if (port->irq < 0)
> > +		return port->irq;
> > +
> > +	irq_set_chained_handler_and_data(port->irq, mtk_pcie_irq_handler, 
> > port);
> 
> You seem to be missing something that will mask all INTx interrupts
> as an initial state.

Yes, the INTx default state of this chip is disabled, but there are
still risks for driver use, I will add mask all INTx interrupts to
mtk_pcie_startup_port function in the next version.

Thanks.
> 
> > +
> > +	return 0;
> > +}
> > +
> >  static int mtk_pcie_clk_init(struct mtk_pcie_port *port)
> >  {
> >  	int ret;
> > @@ -388,6 +545,10 @@ static int mtk_pcie_setup(struct mtk_pcie_port 
> > *port)
> >  		goto err_setup;
> >  	}
> > 
> > +	err = mtk_pcie_setup_irq(port, dev->of_node);
> > +	if (err)
> > +		goto err_setup;
> > +
> >  	dev_info(dev, "PCIe link up success!\n");
> > 
> >  	return 0;
> > @@ -423,6 +584,7 @@ static int mtk_pcie_probe(struct platform_device 
> > *pdev)
> > 
> >  	err = pci_host_probe(host);
> >  	if (err) {
> > +		mtk_pcie_irq_teardown(port);
> >  		mtk_pcie_power_down(port);
> >  		return err;
> >  	}
> > @@ -440,6 +602,7 @@ static int mtk_pcie_remove(struct platform_device 
> > *pdev)
> >  	pci_remove_root_bus(host->bus);
> >  	pci_unlock_rescan_remove();
> > 
> > +	mtk_pcie_irq_teardown(port);
> >  	mtk_pcie_power_down(port);
> > 
> >  	return 0;
> 
> Thanks,
> 
>          M.

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

  reply	other threads:[~2021-01-27 13:12 UTC|newest]

Thread overview: 54+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-01-13 11:39 [v7,0/7] PCI: mediatek: Add new generation controller support Jianjun Wang
2021-01-13 11:39 ` Jianjun Wang
2021-01-13 11:39 ` Jianjun Wang
2021-01-13 11:39 ` [v7,1/7] dt-bindings: PCI: mediatek-gen3: Add YAML schema Jianjun Wang
2021-01-13 11:39   ` Jianjun Wang
2021-01-13 11:39   ` Jianjun Wang
2021-01-25 20:22   ` Rob Herring
2021-01-25 20:22     ` Rob Herring
2021-01-25 20:22     ` Rob Herring
2021-01-26 12:01     ` Jianjun Wang
2021-01-26 12:01       ` Jianjun Wang
2021-01-26 12:01       ` Jianjun Wang
2021-01-13 11:39 ` [v7,2/7] PCI: Export pci_pio_to_address() for module use Jianjun Wang
2021-01-13 11:39   ` Jianjun Wang
2021-01-13 11:39   ` Jianjun Wang
2021-01-13 11:39 ` [v7,3/7] PCI: mediatek-gen3: Add MediaTek Gen3 driver for MT8192 Jianjun Wang
2021-01-13 11:39   ` Jianjun Wang
2021-01-13 11:39   ` Jianjun Wang
2021-01-25 19:54   ` Rob Herring
2021-01-25 19:54     ` Rob Herring
2021-01-25 19:54     ` Rob Herring
2021-01-26 11:42     ` Jianjun Wang
2021-01-26 11:42       ` Jianjun Wang
2021-01-26 11:42       ` Jianjun Wang
2021-01-13 11:39 ` [v7,4/7] PCI: mediatek-gen3: Add INTx support Jianjun Wang
2021-01-13 11:39   ` Jianjun Wang
2021-01-13 11:39   ` Jianjun Wang
2021-01-26 12:25   ` Marc Zyngier
2021-01-26 12:25     ` Marc Zyngier
2021-01-26 12:25     ` Marc Zyngier
2021-01-27 13:09     ` Jianjun Wang [this message]
2021-01-27 13:09       ` Jianjun Wang
2021-01-27 13:09       ` Jianjun Wang
2021-01-13 11:39 ` [v7,5/7] PCI: mediatek-gen3: Add MSI support Jianjun Wang
2021-01-13 11:39   ` Jianjun Wang
2021-01-13 11:39   ` Jianjun Wang
2021-01-26 13:57   ` Marc Zyngier
2021-01-26 13:57     ` Marc Zyngier
2021-01-26 13:57     ` Marc Zyngier
2021-01-27 12:31     ` Jianjun Wang
2021-01-27 12:31       ` Jianjun Wang
2021-01-27 12:31       ` Jianjun Wang
2021-01-27 13:05       ` Marc Zyngier
2021-01-27 13:05         ` Marc Zyngier
2021-01-27 13:05         ` Marc Zyngier
2021-01-27 13:17         ` Jianjun Wang
2021-01-27 13:17           ` Jianjun Wang
2021-01-27 13:17           ` Jianjun Wang
2021-01-13 11:40 ` [v7,6/7] PCI: mediatek-gen3: Add system PM support Jianjun Wang
2021-01-13 11:40   ` Jianjun Wang
2021-01-13 11:40   ` Jianjun Wang
2021-01-13 11:40 ` [v7,7/7] MAINTAINERS: Add Jianjun Wang as MediaTek PCI co-maintainer Jianjun Wang
2021-01-13 11:40   ` Jianjun Wang
2021-01-13 11:40   ` Jianjun Wang

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=1611752954.14672.71.camel@mhfsdcap03 \
    --to=jianjun.wang@mediatek.com \
    --cc=Rex-BC.Chen@mediatek.com \
    --cc=anson.chuang@mediatek.com \
    --cc=bhelgaas@google.com \
    --cc=chuanjia.liu@mediatek.com \
    --cc=devicetree@vger.kernel.org \
    --cc=drinkcat@chromium.org \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-mediatek@lists.infradead.org \
    --cc=linux-pci@vger.kernel.org \
    --cc=lorenzo.pieralisi@arm.com \
    --cc=matthias.bgg@gmail.com \
    --cc=maz@kernel.org \
    --cc=p.zabel@pengutronix.de \
    --cc=qizhong.cheng@mediatek.com \
    --cc=robh+dt@kernel.org \
    --cc=ryder.lee@mediatek.com \
    --cc=sin_jieyang@mediatek.com \
    --cc=sj.huang@mediatek.com \
    --cc=youlin.pei@mediatek.com \
    /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.