Re: [PATCH] pci: introduce common pci config space accessors

From: Rob Herring
Date: Mon Jan 05 2015 - 17:29:16 EST


On Mon, Jan 5, 2015 at 2:01 PM, Arnd Bergmann <arnd@xxxxxxxx> wrote:
> On Monday 05 January 2015 08:46:09 Rob Herring wrote:
>>
>> >> diff --git a/include/linux/pci.h b/include/linux/pci.h
>> >> index 360a966..e7fd519 100644
>> >> --- a/include/linux/pci.h
>> >> +++ b/include/linux/pci.h
>> >> @@ -560,6 +560,7 @@ static inline int pcibios_err_to_errno(int err)
>> >> /* Low-level architecture-dependent routines */
>> >>
>> >> struct pci_ops {
>> >> + void __iomem *(*map_bus)(struct pci_bus *bus, unsigned int devfn, int where);
>> >> int (*read)(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 *val);
>> >> int (*write)(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 val);
>> >> };
>> >
>> > In various other contexts, we have recently discussed adding new callbacks
>> > to struct pci_host_bridge, or an operations structure below it. I don't see
>> > a strong reason for one place or the other, but maybe someone else does.
>> > If we put it into pci_host_bridge_ops, the first argument would need to
>> > be the pci_host_bridge pointer of course.
>>
>> I think it makes sense to keep map_bus together with read/write. Given
>> they are all host specific functions being part of pci_host_bridge
>> would make some sense. However, that would be a pretty painful change
>> across the tree (Have you seen how many PCI host implementations MIPS
>> has?).
>
> We could go wild and do both: if the bus has pci_ops, we use those,
> otherwise we fall back to the map_bus/read/write from pci_host_bridge_ops.

True. I'll leave it for Bjorn to weigh in.

>> > For the common map_bus implementations, it would also be nice to put them
>> > into the same file as your new access functions, but then we need a common
>> > location to store at least one __iomem pointer. I guess that place could
>> > either be struct pci_host_bridge or struct pci_bus. In theory, struct pci_ops
>> > would work too, but then we could no longer mark it 'const' in host bridge
>> > drivers.
>> >
>> > If we have a common set of map_bus functions, we can even export the
>> > pci_ops structures from drivers/pci/access.c:
>> >
>> > const struct pci_ops pci_generic_ecam_ops = {
>> > .map_bus = ecam_map_bus,
>> > .read = pci_generic_config_read,
>> > .write = pci_generic_config_write,
>> > };
>> > EXPORT_SYMBOL_GPL(pci_generic_ecam_ops);
>> >
>> > That could of course be done in a follow-up patch, it doesn't have to be
>> > part of your patch, but it would be good to be prepared.
>>
>> Right, this is what I had in mind for CAM/ECAM. I didn't go this far
>> because a lot of the map_bus functions do various checks to prevent
>> certain accesses. Of what I've found, I think only generic host and
>> Xilinx drivers could be converted to a generic ECAM map_bus. Others
>> check bus number and/or device number or link-up status or have a
>> fixup for certain registers, for example. I'm not sure how much of it
>> is unnecessary or could be common.
>
> How do you want to deal with the overrides? I don't see a way to
> do that in map_bus (with the current definition) if the idea is that
> for certain registers we return hardcoded values instead of accessing
> mmio registers.

It is not done in map_bus unless you want all FFs by returning NULL,
but by simply by wrapping the generic call with a host specific read
or write function. Here's one example that modifies one field in a
register. This means you can do any pre or post processing you need.
Even the crazy stuff Integrator PCI does.

static int cns3xxx_pci_read_config(struct pci_bus *bus, unsigned int devfn,
int where, int size, u32 *val)
{
u32 mask = (0x1ull << (size * 8)) - 1;
int shift = (where % 4) * 8;

ret = pci_generic_config_read32(bus, devfn, where, size, val);

if (ret == PCIBIOS_SUCCESSFUL && bus->number == 0 && devfn == 0 &&
(where & 0xffc) == PCI_CLASS_REVISION)
/*
* RC's class is 0xb, but Linux PCI driver needs 0x604
* for a PCIe bridge. So we must fixup the class code
* to 0x604 here.
*/
*val = ((*val << shift) & 0xff) | (0x604 << 16)) >>
shift) & mask;

return ret;
}

Rob
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/