diff mbox

pci/pciehp: Allow polling/irq mode to be decided on a per-port basis

Message ID 5339FF99.2050200@gmail.com
State Changes Requested
Headers show

Commit Message

Rajat Jain March 31, 2014, 11:51 p.m. UTC
Today, there is a global pciehp_poll_mode module parameter using which
either _all_ the hot-pluggable ports are to use polling, or _all_ the
ports are to use interrupts.

In a system where a certain port has IRQ issues, today the only option
is to use the parameter that converts ALL the ports to use polling mode.
This is not good, and hence this patch intruduces a bit field that can
be set using a PCI quirk that indicates that polling should always be
used for this particular PCIe port. The remaining ports can still
hoose to continue to operate in whatever mode they wish to.

Signed-off-by: Rajat Jain <rajatxjain@gmail.com>
Signed-off-by: Rajat Jain <rajatjain@juniper.net>
Signed-off-by: Guenter Roeck <groeck@juniper.net>
---
 drivers/pci/hotplug/pciehp.h     |    1 +
 drivers/pci/hotplug/pciehp_hpc.c |   16 ++++++++++------
 include/linux/pci.h              |    1 +
 3 files changed, 12 insertions(+), 6 deletions(-)

Comments

Rajat Jain April 14, 2014, 7:42 p.m. UTC | #1
Hello Bjorn,

Was wanting to check your interest on up streaming this one. This one is fairly self-contained and simple, and allows to use polling on a per-port basis.

Please let me know you have any comments.

Thanks,

Rajat

> -----Original Message-----
> From: Rajat Jain [mailto:rajatxjain@gmail.com]
> Sent: Monday, March 31, 2014 4:52 PM
> To: Bjorn Helgaas; linux-pci@vger.kernel.org; linux-kernel@vger.kernel.org
> Cc: Rajat Jain; Guenter Roeck
> Subject: [PATCH] pci/pciehp: Allow polling/irq mode to be decided on a per-
> port basis
> 
> Today, there is a global pciehp_poll_mode module parameter using which
> either _all_ the hot-pluggable ports are to use polling, or _all_ the ports are
> to use interrupts.
> 
> In a system where a certain port has IRQ issues, today the only option is to
> use the parameter that converts ALL the ports to use polling mode.
> This is not good, and hence this patch intruduces a bit field that can be set
> using a PCI quirk that indicates that polling should always be used for this
> particular PCIe port. The remaining ports can still hoose to continue to
> operate in whatever mode they wish to.
> 
> Signed-off-by: Rajat Jain <rajatxjain@gmail.com>
> Signed-off-by: Rajat Jain <rajatjain@juniper.net>
> Signed-off-by: Guenter Roeck <groeck@juniper.net>
> ---
>  drivers/pci/hotplug/pciehp.h     |    1 +
>  drivers/pci/hotplug/pciehp_hpc.c |   16 ++++++++++------
>  include/linux/pci.h              |    1 +
>  3 files changed, 12 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h
> index d208791..753a3b4 100644
> --- a/drivers/pci/hotplug/pciehp.h
> +++ b/drivers/pci/hotplug/pciehp.h
> @@ -98,6 +98,7 @@ struct controller {
>  	unsigned int no_cmd_complete:1;
>  	unsigned int link_active_reporting:1;
>  	unsigned int notification_enabled:1;
> +	unsigned int use_polling:1;	/* Always uses polling for this slot */
>  	unsigned int power_fault_detected;
>  };
> 
> diff --git a/drivers/pci/hotplug/pciehp_hpc.c
> b/drivers/pci/hotplug/pciehp_hpc.c
> index d7d058f..d210d23 100644
> --- a/drivers/pci/hotplug/pciehp_hpc.c
> +++ b/drivers/pci/hotplug/pciehp_hpc.c
> @@ -82,7 +82,7 @@ static inline int pciehp_request_irq(struct controller
> *ctrl)
>  	int retval, irq = ctrl->pcie->irq;
> 
>  	/* Install interrupt polling timer. Start with 10 sec delay */
> -	if (pciehp_poll_mode) {
> +	if (ctrl->use_polling) {
>  		init_timer(&ctrl->poll_timer);
>  		start_int_poll_timer(ctrl, 10);
>  		return 0;
> @@ -98,7 +98,7 @@ static inline int pciehp_request_irq(struct controller
> *ctrl)
> 
>  static inline void pciehp_free_irq(struct controller *ctrl)  {
> -	if (pciehp_poll_mode)
> +	if (ctrl->use_polling)
>  		del_timer_sync(&ctrl->poll_timer);
>  	else
>  		free_irq(ctrl->pcie->irq, ctrl);
> @@ -131,7 +131,7 @@ static int pcie_poll_cmd(struct controller *ctrl)
> 
>  static void pcie_wait_cmd(struct controller *ctrl, int poll)  {
> -	unsigned int msecs = pciehp_poll_mode ? 2500 : 1000;
> +	unsigned int msecs = ctrl->use_polling ? 2500 : 1000;
>  	unsigned long timeout = msecs_to_jiffies(msecs);
>  	int rc;
> 
> @@ -595,7 +595,7 @@ void pcie_enable_notification(struct controller *ctrl)
>  		cmd |= PCI_EXP_SLTCTL_PDCE;
>  	if (MRL_SENS(ctrl))
>  		cmd |= PCI_EXP_SLTCTL_MRLSCE;
> -	if (!pciehp_poll_mode)
> +	if (!ctrl->use_polling)
>  		cmd |= PCI_EXP_SLTCTL_HPIE | PCI_EXP_SLTCTL_CCIE;
> 
>  	mask = (PCI_EXP_SLTCTL_PDCE | PCI_EXP_SLTCTL_ABPE | @@ -
> 642,14 +642,14 @@ int pciehp_reset_slot(struct slot *slot, int probe)
>  	stat_mask |= PCI_EXP_SLTSTA_DLLSC;
> 
>  	pcie_write_cmd(ctrl, 0, ctrl_mask);
> -	if (pciehp_poll_mode)
> +	if (ctrl->use_polling)
>  		del_timer_sync(&ctrl->poll_timer);
> 
>  	pci_reset_bridge_secondary_bus(ctrl->pcie->port);
> 
>  	pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, stat_mask);
>  	pcie_write_cmd(ctrl, ctrl_mask, ctrl_mask);
> -	if (pciehp_poll_mode)
> +	if (ctrl->use_polling)
>  		int_poll_timeout(ctrl->poll_timer.data);
> 
>  	return 0;
> @@ -789,6 +789,10 @@ struct controller *pcie_init(struct pcie_device *dev)
>                  ctrl_dbg(ctrl, "Link Active Reporting supported\n");
>                  ctrl->link_active_reporting = 1;
>          }
> +	if (pciehp_poll_mode || dev->port->hotplug_polling) {
> +		ctrl_info(ctrl, "will use polling\n");
> +		ctrl->use_polling = 1;
> +	}
> 
>  	/* Clear all remaining event bits in Slot Status register */
>  	pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, diff --git
> a/include/linux/pci.h b/include/linux/pci.h index a13d682..b2ec72e 100644
> --- a/include/linux/pci.h
> +++ b/include/linux/pci.h
> @@ -336,6 +336,7 @@ struct pci_dev {
>  	unsigned int	is_virtfn:1;
>  	unsigned int	reset_fn:1;
>  	unsigned int    is_hotplug_bridge:1;
> +	unsigned int    hotplug_polling:1; /* Port uses polling for hotplug */
>  	unsigned int    __aer_firmware_first_valid:1;
>  	unsigned int	__aer_firmware_first:1;
>  	unsigned int	broken_intx_masking:1;
> --
> 1.7.9.5
> 
> 


--
To unsubscribe from this list: send the line "unsubscribe linux-pci" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Bjorn Helgaas April 24, 2014, 9:31 p.m. UTC | #2
On Mon, Mar 31, 2014 at 04:51:53PM -0700, Rajat Jain wrote:
> Today, there is a global pciehp_poll_mode module parameter using which
> either _all_ the hot-pluggable ports are to use polling, or _all_ the
> ports are to use interrupts.
> 
> In a system where a certain port has IRQ issues, today the only option
> is to use the parameter that converts ALL the ports to use polling mode.
> This is not good, and hence this patch intruduces a bit field that can
> be set using a PCI quirk that indicates that polling should always be
> used for this particular PCIe port. The remaining ports can still
> hoose to continue to operate in whatever mode they wish to.
> 
> Signed-off-by: Rajat Jain <rajatxjain@gmail.com>
> Signed-off-by: Rajat Jain <rajatjain@juniper.net>
> Signed-off-by: Guenter Roeck <groeck@juniper.net>

I'm willing to merge this, but I'd prefer to merge it along with a quirk
that actually sets dev->hotplug_polling.  Otherwise it's dead code and I'll
have no way to tell whether we need to keep it.

Bjorn

> ---
>  drivers/pci/hotplug/pciehp.h     |    1 +
>  drivers/pci/hotplug/pciehp_hpc.c |   16 ++++++++++------
>  include/linux/pci.h              |    1 +
>  3 files changed, 12 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h
> index d208791..753a3b4 100644
> --- a/drivers/pci/hotplug/pciehp.h
> +++ b/drivers/pci/hotplug/pciehp.h
> @@ -98,6 +98,7 @@ struct controller {
>  	unsigned int no_cmd_complete:1;
>  	unsigned int link_active_reporting:1;
>  	unsigned int notification_enabled:1;
> +	unsigned int use_polling:1;	/* Always uses polling for this slot */
>  	unsigned int power_fault_detected;
>  };
>  
> diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c
> index d7d058f..d210d23 100644
> --- a/drivers/pci/hotplug/pciehp_hpc.c
> +++ b/drivers/pci/hotplug/pciehp_hpc.c
> @@ -82,7 +82,7 @@ static inline int pciehp_request_irq(struct controller *ctrl)
>  	int retval, irq = ctrl->pcie->irq;
>  
>  	/* Install interrupt polling timer. Start with 10 sec delay */
> -	if (pciehp_poll_mode) {
> +	if (ctrl->use_polling) {
>  		init_timer(&ctrl->poll_timer);
>  		start_int_poll_timer(ctrl, 10);
>  		return 0;
> @@ -98,7 +98,7 @@ static inline int pciehp_request_irq(struct controller *ctrl)
>  
>  static inline void pciehp_free_irq(struct controller *ctrl)
>  {
> -	if (pciehp_poll_mode)
> +	if (ctrl->use_polling)
>  		del_timer_sync(&ctrl->poll_timer);
>  	else
>  		free_irq(ctrl->pcie->irq, ctrl);
> @@ -131,7 +131,7 @@ static int pcie_poll_cmd(struct controller *ctrl)
>  
>  static void pcie_wait_cmd(struct controller *ctrl, int poll)
>  {
> -	unsigned int msecs = pciehp_poll_mode ? 2500 : 1000;
> +	unsigned int msecs = ctrl->use_polling ? 2500 : 1000;
>  	unsigned long timeout = msecs_to_jiffies(msecs);
>  	int rc;
>  
> @@ -595,7 +595,7 @@ void pcie_enable_notification(struct controller *ctrl)
>  		cmd |= PCI_EXP_SLTCTL_PDCE;
>  	if (MRL_SENS(ctrl))
>  		cmd |= PCI_EXP_SLTCTL_MRLSCE;
> -	if (!pciehp_poll_mode)
> +	if (!ctrl->use_polling)
>  		cmd |= PCI_EXP_SLTCTL_HPIE | PCI_EXP_SLTCTL_CCIE;
>  
>  	mask = (PCI_EXP_SLTCTL_PDCE | PCI_EXP_SLTCTL_ABPE |
> @@ -642,14 +642,14 @@ int pciehp_reset_slot(struct slot *slot, int probe)
>  	stat_mask |= PCI_EXP_SLTSTA_DLLSC;
>  
>  	pcie_write_cmd(ctrl, 0, ctrl_mask);
> -	if (pciehp_poll_mode)
> +	if (ctrl->use_polling)
>  		del_timer_sync(&ctrl->poll_timer);
>  
>  	pci_reset_bridge_secondary_bus(ctrl->pcie->port);
>  
>  	pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, stat_mask);
>  	pcie_write_cmd(ctrl, ctrl_mask, ctrl_mask);
> -	if (pciehp_poll_mode)
> +	if (ctrl->use_polling)
>  		int_poll_timeout(ctrl->poll_timer.data);
>  
>  	return 0;
> @@ -789,6 +789,10 @@ struct controller *pcie_init(struct pcie_device *dev)
>                  ctrl_dbg(ctrl, "Link Active Reporting supported\n");
>                  ctrl->link_active_reporting = 1;
>          }
> +	if (pciehp_poll_mode || dev->port->hotplug_polling) {
> +		ctrl_info(ctrl, "will use polling\n");
> +		ctrl->use_polling = 1;
> +	}
>  
>  	/* Clear all remaining event bits in Slot Status register */
>  	pcie_capability_write_word(pdev, PCI_EXP_SLTSTA,
> diff --git a/include/linux/pci.h b/include/linux/pci.h
> index a13d682..b2ec72e 100644
> --- a/include/linux/pci.h
> +++ b/include/linux/pci.h
> @@ -336,6 +336,7 @@ struct pci_dev {
>  	unsigned int	is_virtfn:1;
>  	unsigned int	reset_fn:1;
>  	unsigned int    is_hotplug_bridge:1;
> +	unsigned int    hotplug_polling:1; /* Port uses polling for hotplug */
>  	unsigned int    __aer_firmware_first_valid:1;
>  	unsigned int	__aer_firmware_first:1;
>  	unsigned int	broken_intx_masking:1;
> -- 
> 1.7.9.5
> 
--
To unsubscribe from this list: send the line "unsubscribe linux-pci" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Guenter Roeck April 25, 2014, 4:34 p.m. UTC | #3
> -----Original Message-----
> From: Bjorn Helgaas [mailto:bhelgaas@google.com]
> Sent: Thursday, April 24, 2014 2:31 PM
> To: Rajat Jain
> Cc: linux-pci@vger.kernel.org; linux-kernel@vger.kernel.org; Rajat
> Jain; Guenter Roeck
> Subject: Re: [PATCH] pci/pciehp: Allow polling/irq mode to be decided
> on a per-port basis
> 
> On Mon, Mar 31, 2014 at 04:51:53PM -0700, Rajat Jain wrote:
> > Today, there is a global pciehp_poll_mode module parameter using
> which
> > either _all_ the hot-pluggable ports are to use polling, or _all_ the
> > ports are to use interrupts.
> >
> > In a system where a certain port has IRQ issues, today the only
> option
> > is to use the parameter that converts ALL the ports to use polling
> mode.
> > This is not good, and hence this patch intruduces a bit field that
> can
> > be set using a PCI quirk that indicates that polling should always be
> > used for this particular PCIe port. The remaining ports can still
> > hoose to continue to operate in whatever mode they wish to.
> >
> > Signed-off-by: Rajat Jain <rajatxjain@gmail.com>
> > Signed-off-by: Rajat Jain <rajatjain@juniper.net>
> > Signed-off-by: Guenter Roeck <groeck@juniper.net>
> 
> I'm willing to merge this, but I'd prefer to merge it along with a
> quirk that actually sets dev->hotplug_polling.  Otherwise it's dead
> code and I'll have no way to tell whether we need to keep it.
> 
Bjorn,

what would be the proper location for such a quirk ? 
We use it to help simulating hotplug support on an IDT PES12NT3.
The code is a bit more invasive than just the quirk itself,
since it also needs to touch link and slot status registers,
so quirks.c doesn't seem appropriate.

drivers/pci/pes12nt3.c, maybe, with a separate configuration
option ? Or in the hotplug directory ?

Guenter

--
To unsubscribe from this list: send the line "unsubscribe linux-pci" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Bjorn Helgaas April 25, 2014, 4:43 p.m. UTC | #4
On Fri, Apr 25, 2014 at 10:34 AM, Guenter Roeck <groeck@juniper.net> wrote:
>
>
>> -----Original Message-----
>> From: Bjorn Helgaas [mailto:bhelgaas@google.com]
>> Sent: Thursday, April 24, 2014 2:31 PM
>> To: Rajat Jain
>> Cc: linux-pci@vger.kernel.org; linux-kernel@vger.kernel.org; Rajat
>> Jain; Guenter Roeck
>> Subject: Re: [PATCH] pci/pciehp: Allow polling/irq mode to be decided
>> on a per-port basis
>>
>> On Mon, Mar 31, 2014 at 04:51:53PM -0700, Rajat Jain wrote:
>> > Today, there is a global pciehp_poll_mode module parameter using
>> which
>> > either _all_ the hot-pluggable ports are to use polling, or _all_ the
>> > ports are to use interrupts.
>> >
>> > In a system where a certain port has IRQ issues, today the only
>> option
>> > is to use the parameter that converts ALL the ports to use polling
>> mode.
>> > This is not good, and hence this patch intruduces a bit field that
>> can
>> > be set using a PCI quirk that indicates that polling should always be
>> > used for this particular PCIe port. The remaining ports can still
>> > hoose to continue to operate in whatever mode they wish to.
>> >
>> > Signed-off-by: Rajat Jain <rajatxjain@gmail.com>
>> > Signed-off-by: Rajat Jain <rajatjain@juniper.net>
>> > Signed-off-by: Guenter Roeck <groeck@juniper.net>
>>
>> I'm willing to merge this, but I'd prefer to merge it along with a
>> quirk that actually sets dev->hotplug_polling.  Otherwise it's dead
>> code and I'll have no way to tell whether we need to keep it.
>>
> Bjorn,
>
> what would be the proper location for such a quirk ?
> We use it to help simulating hotplug support on an IDT PES12NT3.
> The code is a bit more invasive than just the quirk itself,
> since it also needs to touch link and slot status registers,
> so quirks.c doesn't seem appropriate.
>
> drivers/pci/pes12nt3.c, maybe, with a separate configuration
> option ? Or in the hotplug directory ?

If this is only for debug, i.e., you don't intend to ship a product
using this simulated hotplug, maybe you should just keep both the
quirk and this patch out of tree.

If you do want to eventually ship this code for some product, I think
it'd be fine to put the quirk in drivers/pci/quirks.c, maybe with a
config option to enable it.  But without seeing the quirk, I can't
really tell.  A new file seems overkill unless it's something really
huge -- I don't think we really have examples of dedicated files for
other chip idiosyncrasies.

Bjorn
--
To unsubscribe from this list: send the line "unsubscribe linux-pci" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Guenter Roeck April 25, 2014, 5:37 p.m. UTC | #5
> -----Original Message-----

> From: Bjorn Helgaas [mailto:bhelgaas@google.com]

> Sent: Friday, April 25, 2014 9:44 AM

> To: Guenter Roeck

> Cc: Rajat Jain; linux-pci@vger.kernel.org; linux-

> kernel@vger.kernel.org; Rajat Jain

> Subject: Re: [PATCH] pci/pciehp: Allow polling/irq mode to be decided

> on a per-port basis

> 

> On Fri, Apr 25, 2014 at 10:34 AM, Guenter Roeck <groeck@juniper.net>

> wrote:

> >

> >

> >> -----Original Message-----

> >> From: Bjorn Helgaas [mailto:bhelgaas@google.com]

> >> Sent: Thursday, April 24, 2014 2:31 PM

> >> To: Rajat Jain

> >> Cc: linux-pci@vger.kernel.org; linux-kernel@vger.kernel.org; Rajat

> >> Jain; Guenter Roeck

> >> Subject: Re: [PATCH] pci/pciehp: Allow polling/irq mode to be

> decided

> >> on a per-port basis

> >>

> >> On Mon, Mar 31, 2014 at 04:51:53PM -0700, Rajat Jain wrote:

> >> > Today, there is a global pciehp_poll_mode module parameter using

> >> which

> >> > either _all_ the hot-pluggable ports are to use polling, or _all_

> >> > the ports are to use interrupts.

> >> >

> >> > In a system where a certain port has IRQ issues, today the only

> >> option

> >> > is to use the parameter that converts ALL the ports to use polling

> >> mode.

> >> > This is not good, and hence this patch intruduces a bit field that

> >> can

> >> > be set using a PCI quirk that indicates that polling should always

> >> > be used for this particular PCIe port. The remaining ports can

> >> > still hoose to continue to operate in whatever mode they wish to.

> >> >

> >> > Signed-off-by: Rajat Jain <rajatxjain@gmail.com>

> >> > Signed-off-by: Rajat Jain <rajatjain@juniper.net>

> >> > Signed-off-by: Guenter Roeck <groeck@juniper.net>

> >>

> >> I'm willing to merge this, but I'd prefer to merge it along with a

> >> quirk that actually sets dev->hotplug_polling.  Otherwise it's dead

> >> code and I'll have no way to tell whether we need to keep it.

> >>

> > Bjorn,

> >

> > what would be the proper location for such a quirk ?

> > We use it to help simulating hotplug support on an IDT PES12NT3.

> > The code is a bit more invasive than just the quirk itself, since it

> > also needs to touch link and slot status registers, so quirks.c

> > doesn't seem appropriate.

> >

> > drivers/pci/pes12nt3.c, maybe, with a separate configuration option ?

> > Or in the hotplug directory ?

> 

> If this is only for debug, i.e., you don't intend to ship a product

> using this simulated hotplug, maybe you should just keep both the quirk

> and this patch out of tree.

> 

> If you do want to eventually ship this code for some product, I think

> it'd be fine to put the quirk in drivers/pci/quirks.c, maybe with a

> config option to enable it.  But without seeing the quirk, I can't

> really tell.  A new file seems overkill unless it's something really

> huge -- I don't think we really have examples of dedicated files for

> other chip idiosyncrasies.

> 


I'd give it a 50:50 probability that it will ship. Current plan is that
it is for development only, but I suspect that may change at some point.

I agree, this is kind of an outlier. If we push it upstream, it might
mostly serve as a reference for others who might have similar problems -
not just for the quirk itself, but as an example on how to intercept
and manipulate pci configuration register accesses.

I attached the file so you can have a look.

Guenter
/*
 * PTX5000  SIB - PCI fixup code
 *
 * Rajat Jain <rajatjain@juniper.net>
 * Copyright 2014 Juniper Networks
 *
 * This program is free software; you can redistribute  it and/or modify it
 * under the terms of the GNU General Public License v2 as published by the
 * Free Software Foundation
 */

#include <linux/list.h>
#include <linux/pci.h>
#include <linux/jnx/pci_ids.h>
#include <linux/spinlock.h>

#define IDT_PES12NT3_DLSTS              0x268
#define IDT_PES12NT3_DLSTS_DLFSM        0x7
#define IDT_PES12NT3_DLSTS_LINKACTIVE   0x4

struct pes12nt3_pci_data {
	struct list_head list;
	struct device *dev;
	struct pci_ops ops;
	struct pci_ops *old_ops;
	bool lnksta_dllla;
	bool sltsta_dllsc;
};

static LIST_HEAD(pes12nt3_list);
static DEFINE_SPINLOCK(pes12nt3_lock);

static int pes12nt3_update_linkstatus(struct pes12nt3_pci_data *data,
				      struct pci_bus *bus, unsigned int devfn)
{
	u32 linkactive;
	bool dllla;
	int retval;

	retval = data->old_ops->read(bus, devfn, IDT_PES12NT3_DLSTS,
				     4, &linkactive);
	if (retval)
		return retval;
	if (linkactive == 0xffffffff)
		dllla = false;
	else
		dllla = (linkactive & IDT_PES12NT3_DLSTS_DLFSM) ==
						IDT_PES12NT3_DLSTS_LINKACTIVE;
	if (dllla != data->lnksta_dllla)
		data->sltsta_dllsc = true;
	data->lnksta_dllla = dllla;
	return 0;
}

static int pes12nt3_pci_read(struct pci_bus *bus, unsigned int devfn,
			      int where, int size, u32 *val)
{
	struct pes12nt3_pci_data *data =
			container_of(bus->ops, struct pes12nt3_pci_data, ops);
	int retval, pos;

	retval = data->old_ops->read(bus, devfn, where, size, val);

	/* Only need to change the registers at port leading to TF chip */
	if (retval || devfn != 0)
		return retval;

	retval = pes12nt3_update_linkstatus(data, bus, devfn);
	if (retval)
		return retval;

	pos = where - 0x40;

	/*
	 * PCI registers smaller than 32 bits may be read using
	 * different lengths at diferent offsets. Consider:
	 *
	 * +-----------------------------------+
	 * |  Reg-1 |  Reg-2 |  Reg-3 |  Reg-4 |
	 * +-----------------------------------+
	 * ^        ^        ^        ^
	 * ptr      ptr+1    ptr+2    ptr+3
	 *
	 * Reg-4 may be read by using a:
	 * 1 byte read at (ptr+3)
	 * 2 byte read at (ptr+2)
	 * 4 byte read at (ptr)
	 * etc
	 *
	 * We need to take of this here.
	 */

	switch (size) {
	case 4:
		switch (pos) {
		case 0:
			if (*val == 0xffffffff)
				*val = 0;
			else
				*val |= (PCI_EXP_FLAGS_SLOT << 16);
			break;
		case PCI_EXP_LNKCAP:
			if (*val == 0xffffffff)
				*val = 0;
			else
				*val |= PCI_EXP_LNKCAP_DLLLARC;
			break;
		case PCI_EXP_SLTCAP:
			if (*val == 0xffffffff) {
				*val = 0;
			} else {
				*val |= PCI_EXP_SLTCAP_HPC;
				*val = (*val & ~PCI_EXP_SLTCAP_PSN) |
					(bus->number << 19);
			}
			break;
		case PCI_EXP_LNKCTL:
			if (*val == 0xffffffff) {
				*val = 0;
			} else {
				if (data->lnksta_dllla)
					*val |= PCI_EXP_LNKSTA_DLLLA << 16;
				else
					*val &= ~(PCI_EXP_LNKSTA_DLLLA << 16);
			}
			break;
		}
		break;

	case 2:
		switch (pos) {
		case PCI_EXP_FLAGS_SLOT:
			if (*val == 0xffff)
				*val = 0;
			else
				*val |= PCI_EXP_FLAGS_SLOT;
			break;
		case PCI_EXP_LNKSTA:
			if (*val == 0xffff) {
				*val = 0;
			} else {
				if (data->lnksta_dllla)
					*val |= PCI_EXP_LNKSTA_DLLLA;
				else
					*val &= ~PCI_EXP_LNKSTA_DLLLA;
			}
			break;
		case PCI_EXP_SLTSTA:
			if (*val == 0xffff)
				*val = PCI_EXP_SLTSTA_DLLSC;
			else if (data->sltsta_dllsc)
				*val |= PCI_EXP_SLTSTA_DLLSC;
			break;
		}
		break;
	}
	return 0;
}

static int pes12nt3_pci_write(struct pci_bus *bus, unsigned int devfn,
			      int where, int size, u32 val)
{
	struct pes12nt3_pci_data *data =
			container_of(bus->ops, struct pes12nt3_pci_data, ops);
	int pos = where - 0x40;
	int retval;

	if (devfn == 0 && size == 2) {
		switch (pos) {
		case PCI_EXP_SLTSTA:
			if (val & PCI_EXP_SLTSTA_DLLSC)
				data->sltsta_dllsc = false;
			break;
		}
	}

	retval = data->old_ops->write(bus, devfn, where, size, val);
	if (retval || devfn != 0)
		return retval;

	/* Catch situations where the link status changed after being handled */
	return pes12nt3_update_linkstatus(data, bus, devfn);
}

static void pes12nt3_fake_linkstate_hotplug(struct pci_dev *dev)
{
	struct pes12nt3_pci_data *data;

	if (pci_pcie_type(dev) == PCI_EXP_TYPE_UPSTREAM) {
		data = kzalloc(sizeof(*data), GFP_KERNEL);
		if (data == NULL)
			return;
		data->ops.read = pes12nt3_pci_read;
		data->ops.write = pes12nt3_pci_write;
		data->old_ops = pci_bus_set_ops(dev->subordinate, &data->ops);
		data->dev = &dev->dev;
		spin_lock(&pes12nt3_lock);
		list_add(&data->list, &pes12nt3_list);
		spin_unlock(&pes12nt3_lock);
	} else if (pci_pcie_type(dev) == PCI_EXP_TYPE_DOWNSTREAM) {
		dev->hotplug_polling = 1; /* No IRQ support */
		dev->pcie_flags_reg |= PCI_EXP_FLAGS_SLOT;
	}
}
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_IDT, PCI_DEVICE_ID_IDT_PES12NT3_TRANS_AB,
			pes12nt3_fake_linkstate_hotplug);

static void pes12nt3_cleanup_entry(struct device *dev)
{
	struct pes12nt3_pci_data *data, *tmp;

	spin_lock(&pes12nt3_lock);
	list_for_each_entry_safe(data, tmp, &pes12nt3_list, list) {
		if (data->dev == dev) {
			list_del(&data->list);
			kfree(data);
			break;
		}
	}
	spin_unlock(&pes12nt3_lock);
}

static int pes12nt3_notifier_call(struct notifier_block *n,
				  unsigned long action, void *dev)
{
	if (action == BUS_NOTIFY_DEL_DEVICE)
		pes12nt3_cleanup_entry(dev);

	return NOTIFY_DONE;
}

static struct notifier_block pes12nt3_nb = {
	.notifier_call = pes12nt3_notifier_call,
};

static int __init pes12nt3_init(void)
{
	bus_register_notifier(&pci_bus_type, &pes12nt3_nb);
	return 0;
}

subsys_initcall(pes12nt3_init);
diff mbox

Patch

diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h
index d208791..753a3b4 100644
--- a/drivers/pci/hotplug/pciehp.h
+++ b/drivers/pci/hotplug/pciehp.h
@@ -98,6 +98,7 @@  struct controller {
 	unsigned int no_cmd_complete:1;
 	unsigned int link_active_reporting:1;
 	unsigned int notification_enabled:1;
+	unsigned int use_polling:1;	/* Always uses polling for this slot */
 	unsigned int power_fault_detected;
 };
 
diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c
index d7d058f..d210d23 100644
--- a/drivers/pci/hotplug/pciehp_hpc.c
+++ b/drivers/pci/hotplug/pciehp_hpc.c
@@ -82,7 +82,7 @@  static inline int pciehp_request_irq(struct controller *ctrl)
 	int retval, irq = ctrl->pcie->irq;
 
 	/* Install interrupt polling timer. Start with 10 sec delay */
-	if (pciehp_poll_mode) {
+	if (ctrl->use_polling) {
 		init_timer(&ctrl->poll_timer);
 		start_int_poll_timer(ctrl, 10);
 		return 0;
@@ -98,7 +98,7 @@  static inline int pciehp_request_irq(struct controller *ctrl)
 
 static inline void pciehp_free_irq(struct controller *ctrl)
 {
-	if (pciehp_poll_mode)
+	if (ctrl->use_polling)
 		del_timer_sync(&ctrl->poll_timer);
 	else
 		free_irq(ctrl->pcie->irq, ctrl);
@@ -131,7 +131,7 @@  static int pcie_poll_cmd(struct controller *ctrl)
 
 static void pcie_wait_cmd(struct controller *ctrl, int poll)
 {
-	unsigned int msecs = pciehp_poll_mode ? 2500 : 1000;
+	unsigned int msecs = ctrl->use_polling ? 2500 : 1000;
 	unsigned long timeout = msecs_to_jiffies(msecs);
 	int rc;
 
@@ -595,7 +595,7 @@  void pcie_enable_notification(struct controller *ctrl)
 		cmd |= PCI_EXP_SLTCTL_PDCE;
 	if (MRL_SENS(ctrl))
 		cmd |= PCI_EXP_SLTCTL_MRLSCE;
-	if (!pciehp_poll_mode)
+	if (!ctrl->use_polling)
 		cmd |= PCI_EXP_SLTCTL_HPIE | PCI_EXP_SLTCTL_CCIE;
 
 	mask = (PCI_EXP_SLTCTL_PDCE | PCI_EXP_SLTCTL_ABPE |
@@ -642,14 +642,14 @@  int pciehp_reset_slot(struct slot *slot, int probe)
 	stat_mask |= PCI_EXP_SLTSTA_DLLSC;
 
 	pcie_write_cmd(ctrl, 0, ctrl_mask);
-	if (pciehp_poll_mode)
+	if (ctrl->use_polling)
 		del_timer_sync(&ctrl->poll_timer);
 
 	pci_reset_bridge_secondary_bus(ctrl->pcie->port);
 
 	pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, stat_mask);
 	pcie_write_cmd(ctrl, ctrl_mask, ctrl_mask);
-	if (pciehp_poll_mode)
+	if (ctrl->use_polling)
 		int_poll_timeout(ctrl->poll_timer.data);
 
 	return 0;
@@ -789,6 +789,10 @@  struct controller *pcie_init(struct pcie_device *dev)
                 ctrl_dbg(ctrl, "Link Active Reporting supported\n");
                 ctrl->link_active_reporting = 1;
         }
+	if (pciehp_poll_mode || dev->port->hotplug_polling) {
+		ctrl_info(ctrl, "will use polling\n");
+		ctrl->use_polling = 1;
+	}
 
 	/* Clear all remaining event bits in Slot Status register */
 	pcie_capability_write_word(pdev, PCI_EXP_SLTSTA,
diff --git a/include/linux/pci.h b/include/linux/pci.h
index a13d682..b2ec72e 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -336,6 +336,7 @@  struct pci_dev {
 	unsigned int	is_virtfn:1;
 	unsigned int	reset_fn:1;
 	unsigned int    is_hotplug_bridge:1;
+	unsigned int    hotplug_polling:1; /* Port uses polling for hotplug */
 	unsigned int    __aer_firmware_first_valid:1;
 	unsigned int	__aer_firmware_first:1;
 	unsigned int	broken_intx_masking:1;