Re: [PATCH v2 2/5] arm/pci: Scan PCI root bus for devices

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



'arm' shouldn't be in the subject of this patch.

On Thu, Feb 11, 2016 at 09:25:02AM +0100, Alexander Gordeev wrote:
> Scan bus 0 only and function 0 only on each device.
> 
> Operations pci_config_read*/pci_config_write* are just
> wrappers over read*/write* accessors and only needed
> to emphasize PCI configuration space access.
> 
> Cc: Thomas Huth <thuth@xxxxxxxxxx>
> Cc: Andrew Jones <drjones@xxxxxxxxxx>
> Signed-off-by: Alexander Gordeev <agordeev@xxxxxxxxxx>
> ---
>  lib/pci-host-generic.c | 74 +++++++++++++++++++++++++++++++++++++++++++++++++-
>  lib/pci-host-generic.h | 34 +++++++++++++++++++++++
>  2 files changed, 107 insertions(+), 1 deletion(-)
> 
> diff --git a/lib/pci-host-generic.c b/lib/pci-host-generic.c
> index 9bc6642..fd3bb34 100644
> --- a/lib/pci-host-generic.c
> +++ b/lib/pci-host-generic.c
> @@ -38,6 +38,18 @@ static pci_res_type_t flags_to_type(u32 of_flags)
>  	return ((of_flags & 0x40000000) >> 28) | ((of_flags >> 24) & 0x03);
>  }
>  
> +int find_next_dev(struct gen_pci *pci, int dev)
> +{
> +	for (dev++; dev < PCI_NUM_DEVICES; dev++) {
> +		void *conf = dev_conf(pci, dev);
> +
> +		if (pci_config_readl(conf, PCI_VENDOR_ID) != ((u32)~0))
> +			return dev;
> +	}
> +
> +	return -1;
> +}

find_next_dev, used by for_each_pci_dev, seems a bit crufty to me. Why
not just have

for (i = 0; i < 256; ++i)
   if ([get pci-vendor-id] == 0xffff)
      continue;

everywhere you need to iterate the devices?

> +
>  static u32 from_fdt32(fdt32_t val)
>  {
>  	return fdt32_to_cpu(val);
> @@ -199,20 +211,80 @@ static struct gen_pci *gen_pci_probe(void)
>  	return pci;
>  }
>  
> +static void pci_dev_print(void *conf)
> +{
> +	u16 vendor_id = pci_config_readw(conf, PCI_VENDOR_ID);
> +	u16 device_id = pci_config_readw(conf, PCI_DEVICE_ID);
> +	u8 header = pci_config_readb(conf, PCI_HEADER_TYPE);
> +	u8 progif = pci_config_readb(conf, PCI_CLASS_PROG);
> +	u8 subcl = pci_config_readb(conf, PCI_CLASS_DEVICE);
> +	u8 class = pci_config_readb(conf, PCI_CLASS_DEVICE + 1);
> +
> +	printf("conf %p vendor_id %04x device_id %04x type %d "
> +	       "progif %02x class %02x subcl %02x\n",
> +	       conf, vendor_id, device_id, header,
> +	       progif, class, subcl);
> +}

This should also be in pci.c, and globally accessible for unittests.
It also needs to be written to use the global 'struct pci *pci'
object.

> +
> +static int pci_bus_scan(struct gen_pci *pci)
> +{
> +	int dev;
> +	int nr_dev = 0;
> +
> +	if (!pci)
> +		return -1;

This check is unnecessary, as this is a private function. We don't
have any other error conditions, so this should be a void function.

> +
> +	for_each_pci_dev(pci, dev) {
> +		void *conf = dev_conf(pci, dev);
> +		pci_dev_print(conf);

We shouldn't print on every unittest boot. But providing the means to
print is good. Perhaps we need a compile-time flag providing a verbose
mode?

> +
> +		/* We are only interested in normal PCI devices */
> +		if (pci_config_readb(conf, PCI_HEADER_TYPE) !=
> +					   PCI_HEADER_TYPE_NORMAL)
> +			continue;
> +
> +		pci_config_writel(PCI_COMMAND_SERR, conf, PCI_COMMAND);
> +		nr_dev++;
> +	}
> +
> +	return nr_dev;
> +}
> +
>  bool pci_probe(void)

This should return the pci pointer.

>  {
>  	struct gen_pci *pci = get_pci();

Don't need the get_pci() call, we have access to the private
pointer directly.

>  
>  	assert(pci == NULL);
>  	pci = gen_pci_probe();
> +	if (!pci)
> +		goto probe;

return NULL;

>  	set_pci(pci);
>  
> -	return (pci != NULL);
> +	if (pci_bus_scan(pci) < 0)
> +		goto scan;

This is a void function, no need for goto.

return pci;

No need for labels below.
> +
> +	return true;
> +
> +scan:
> +	pci_shutdown();
> +
> +probe:
> +	return false;
>  }
>  
>  void pci_shutdown(void)
>  {
>  	struct gen_pci *pci = get_pci();

get_pci not needed.

> +	int dev;
> +
> +	if (!pci)
> +		return;
> +
> +	for_each_pci_dev(pci, dev) {
> +		void *conf = dev_conf(pci, dev);
> +
> +		pci_config_writel(PCI_COMMAND_INTX_DISABLE, conf, PCI_COMMAND);
> +	}

Should be written in pci.c using pci_config_read/write functions.

>  
>  	set_pci(NULL);
>  	free(pci);
> diff --git a/lib/pci-host-generic.h b/lib/pci-host-generic.h
> index f4ae3e4..1d86b43 100644
> --- a/lib/pci-host-generic.h
> +++ b/lib/pci-host-generic.h
> @@ -69,5 +69,39 @@ typedef enum pci_res_type {
>   *
>   */
>  #define PCI_ECAM_BUS_SIZE	(1 << 20)
> +#define PCI_NUM_DEVICES		(PCI_ECAM_BUS_SIZE / (1 << 15))

Weird way to write 32 :-)

> +#define PCI_ECAM_CONFIG_SIZE	(1 << 12)
> +
> +#define for_each_pci_dev(pci, dev)		\
> +	for (dev = find_next_dev(pci, -1);	\
> +	     dev >= 0;				\
> +	     dev = find_next_dev(pci, dev))
> +
> +static void* dev_conf(struct gen_pci *pci, int dev)

This function needs pci_ prefixing it, and I would name it 
something like pci_get_config

> +{
> +	return (void*)pci->cpu_range.start + (dev * 8) * PCI_ECAM_CONFIG_SIZE;

Wait. This should be the implementation of that nice comment about
cfg_offset, right? Shouldn't it be

static void *pci_get_conf(struct gen_pci *pci, int dev, int register)
{
  pci_host_bridge *host = container_of(pci, struct pci_host_bridge, pci);
  unsigned offset = /* bus << 20 | */ dev << 15 | /* function << 12 | */ register;
  return host->cpu_range.start + offset;
}

Of course (dev * 8 * 4096) results in the same thing (not including
register) as (dev << 15)...

(I actually would rather not have the extra cpu_range part in the
 pci_host_bridge either. I don't think it gains much in readability,
 to just doing host->addr, or some such.)

> +}
> +
> +static u8 pci_config_readb(const void *conf, int off)
> +{
> +	return readb(conf + off);
> +}
> +
> +static u16 pci_config_readw(const void *conf, int off)
> +{
> +	return readw(conf + off);
> +}
> +
> +static u32 pci_config_readl(const void *conf, int off)
> +{
> +	return readl(conf + off);
> +}
> +
> +static void pci_config_writel(u32 val, void *conf, int off)
> +{
> +	writel(val, conf + off);
> +}

There's no point to these wrappers. We need a way to get the config
address, like "pci_get_config", but then we can just use readl/writel
directly from within lib/pci-host-generic.c. lib/pci.c should always
us something like pci_config_read(), which is implemented in the arch-
specific header lib/<ARCH>/asm/pci.h, and thus can be something that
uses readl/writel directly too when we're an arch using pci-host-bridge.

> +
> +extern int find_next_dev(struct gen_pci *pci, int dev);
>  
>  #endif
> -- 
> 1.8.3.1
>

Thanks,
drew 
--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [KVM ARM]     [KVM ia64]     [KVM ppc]     [Virtualization Tools]     [Spice Development]     [Libvirt]     [Libvirt Users]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite Questions]     [Linux Kernel]     [Linux SCSI]     [XFree86]
  Powered by Linux