CFT/RFC: New cardbus resource allocation

CFT/RFC: New cardbus resource allocation

Post by Russell Kin » Thu, 07 Nov 2002 21:50:07



The following is an experimental patch adjusts the way we allocate
resouces for PCMCIA cards in cardbus devices.

When a PCMCIA driver wishes to claim resources for a card, the original
code used to use an in-kernel structured list to find a free area of
memory, mostly ignoring the Linux resource subsystem (although it did
make use of the resource subsystem, it's racy.)

The following patch changes this.  We instead use the PCI resource
code to allocate a resource of the requested size, alignment, and
offset from the parent bus of the cardbus bridge.

Why do we need the size, alignment and offset?

Some PCMCIA devices have specific requirements for IO addresses; some
cards only decode 10 bits, and expect these 10 bits to be one of a
selection of addresses.  This means that the top 6 bits are ignored,
and can be any value.  However, the lower 10 bits must be one of a
fixed set of values, eg 0x3f8, 0x3e8, 0x2f8, 0x2e8.

Hence, 0x3f8, 0x7f8, 0xbf8 etc are all possible values, which obviously
gives you an alignment of (1 << 10) and an offset of 0x3f8.

(Appologies in advance if I've messed this patch up - it got dug it
out of a pile of other patches in this area.)

diff -ur orig/drivers/pci/pci.c linux/drivers/pci/pci.c
--- orig/drivers/pci/pci.c      Tue Nov  5 12:51:22 2002
+++ linux/drivers/pci/pci.c     Tue Nov  5 12:55:26 2002
@@ -659,6 +659,7 @@
 EXPORT_SYMBOL(pci_clear_mwi);
 EXPORT_SYMBOL(pci_set_dma_mask);
 EXPORT_SYMBOL(pci_dac_set_dma_mask);
+EXPORT_SYMBOL(pci_alloc_parent_resource);
 EXPORT_SYMBOL(pci_assign_resource);
 EXPORT_SYMBOL(pci_find_parent_resource);

diff -ur orig/drivers/pci/setup-res.c linux/drivers/pci/setup-res.c
--- orig/drivers/pci/setup-res.c        Thu Oct  3 12:43:08 2002
+++ linux/drivers/pci/setup-res.c       Wed Oct 30 19:40:33 2002
@@ -57,6 +57,47 @@
 }

 /*
+ * Given the PCI bus a device resides on, the size, minimum address,
+ * alignment and type, try to find an acceptable resource allocation
+ * for a specific device resource.
+ */
+int
+pci_alloc_parent_resource(struct pci_dev *dev, struct resource *res,
+                         unsigned long size, unsigned long align,
+                         unsigned long min,
+                         void (*alignf)(void *, struct resource *,
+                                        unsigned long, unsigned long),
+                         void *alignf_data)
+{
+       struct pci_bus *bus = dev->bus;
+       int i;
+
+       for (i = 0; i < PCI_BUS_NUM_RESOURCES; i++) {
+               struct resource *r = bus->resource[i];
+               if (!r)
+                       continue;
+
+               /* type_mask must match */
+               if ((res->flags ^ r->flags) & (IORESOURCE_IO | IORESOURCE_MEM))
+                       continue;
+
+               /* We cannot allocate a non-prefetching resource
+                  from a prefetching area */
+               if ((r->flags & IORESOURCE_PREFETCH) &&
+                   !(res->flags & IORESOURCE_PREFETCH))
+                       continue;
+
+               /* Ok, try it out.. */
+               if (allocate_resource(r, res, size, min, -1, align,
+                                     alignf, alignf_data) < 0)
+                       continue;
+
+               return 0;
+       }
+       return -ENOMEM;
+}
+
+/*
  * Given the PCI bus a device resides on, try to
  * find an acceptable resource allocation for a
  * specific device resource..
@@ -72,8 +113,13 @@
        unsigned long align;
        int i;

+       /* The bridge resources are special, as their
+          size != alignment. Sizing routines return
+          required alignment in the "start" field. */
+       align = (resno < PCI_BRIDGE_RESOURCES) ? size : res->start;
+
        type_mask |= IORESOURCE_IO | IORESOURCE_MEM;
-       for (i = 0 ; i < PCI_BUS_NUM_RESOURCES; i++) {
+       for (i = 0; i < PCI_BUS_NUM_RESOURCES; i++) {
                struct resource *r = bus->resource[i];
                if (!r)
                        continue;
@@ -87,11 +133,6 @@
                if ((r->flags & IORESOURCE_PREFETCH) &&
                    !(res->flags & IORESOURCE_PREFETCH))
                        continue;
-
-               /* The bridge resources are special, as their
-                  size != alignment. Sizing routines return
-                  required alignment in the "start" field. */
-               align = (resno < PCI_BRIDGE_RESOURCES) ? size : res->start;

                /* Ok, try it out.. */
                if (allocate_resource(r, res, size, min, -1, align,
diff -ur orig/drivers/pcmcia/cardbus.c linux/drivers/pcmcia/cardbus.c
--- orig/drivers/pcmcia/cardbus.c       Sat Jul  6 17:36:12 2002
+++ linux/drivers/pcmcia/cardbus.c      Wed Nov  6 15:16:15 2002
@@ -418,3 +418,105 @@
        /* Turn off bridge windows */
        cb_release_cis_mem(s);
 }
+
+
+
+static struct resource *
+make_resource(unsigned long b, unsigned long n, int flags, char *name)
+{
+       struct resource *res = kmalloc(sizeof(struct resource), GFP_KERNEL);
+
+       if (res) {
+               memset(res, 0, sizeof(*res));
+               res->name = name;
+               res->start = b;
+               res->end = b + n - 1;
+               res->flags = flags | IORESOURCE_BUSY;
+       }
+       return res;
+}
+
+struct cardbus_alignment {
+       unsigned long mask;
+       unsigned long offset;
+};
+
+/*
+ * Some PCMCIA cards have only a limited number of address lines, and
+ * require a certain bit pattern on the high lines.  This gives us the
+ * following requirement for an address:
+ *
+ *     (base & ~((1 << bits) - 1)) + offset
+ *
+ * The initial alignment is taken care of in the generic resource
+ * allocation code, so we just need to ensure the correct offset.
+ *
+ * Also, if the PCMCIA card requests a resource with a specific offset
+ * less than our normal minimum, we assume that this is because it knows
+ * it must have that address.
+ */
+static void cardbus_adjust(void *data, struct resource *res,
+                          unsigned long size, unsigned long align)
+{
+       struct cardbus_alignment *ca = data;
+       unsigned long start;
+      
+       start = (res->start & ~ca->mask) + ca->offset;
+       if (start < res->start)
+               start = ((res->start + ca->mask) & ~ca->mask) + ca->offset;
+
+       res->start = start;
+}
+
+int cb_alloc_io(ioaddr_t *base, ioaddr_t num, ioaddr_t align, char *name,
+               socket_info_t *s)
+{
+       struct resource *res = make_resource(0, num, IORESOURCE_IO, name);
+       struct cardbus_alignment ca;
+       unsigned long min;
+       int ret;
+
+       ca.mask = align - 1;
+       ca.offset = *base & ca.mask;
+
+       if (*base && *base < PCIBIOS_MIN_IO) {
+               min = *base;
+       } else {
+               min = PCIBIOS_MIN_IO;
+       }
+
+       ret = pci_alloc_parent_resource(s->cap.cb_dev, res, num, 1,
+                                       min, cardbus_adjust, &ca);
+       if (ret == 0) {
+               *base = res->start;
+       } else {
+               kfree(res);
+       }
+       return ret;
+}
+
+int cb_alloc_mem(unsigned long *base, unsigned long num, unsigned long align,
+                char *name, socket_info_t *s)
+{
+       struct resource *res = make_resource(0, num, IORESOURCE_MEM, name);
+       struct cardbus_alignment ca;
+       unsigned long min;
+       int ret;
+
+       ca.mask = align - 1;
+       ca.offset = *base & ca.mask;
+
+       if (*base && *base < PCIBIOS_MIN_MEM)
+               min = *base;
+       else
+               min = PCIBIOS_MIN_MEM;
+
+       ret = pci_alloc_parent_resource(s->cap.cb_dev, res, num, 1,
+                                       min, cardbus_adjust, &ca);
+       if (ret == 0) {
+               *base = RESOURCE_TO_BUS(res->start);
+       } else {
+               kfree(res);
+       }
+       return ret;
+}
diff -ur orig/drivers/pcmcia/cs.c linux/drivers/pcmcia/cs.c
--- orig/drivers/pcmcia/cs.c    Sat Oct 12 10:00:50 2002
+++ linux/drivers/pcmcia/cs.c   Thu Oct 24 23:06:28 2002
@@ -483,6 +483,7 @@
     cb_release_cis_mem(s);
     cb_free(s);
 #endif
+    release_cis_mem(s);
     s->functions = 0;
     if (s->config) {
        kfree(s->config);
diff -ur orig/drivers/pcmcia/rsrc_mgr.c linux/drivers/pcmcia/rsrc_mgr.c
--- orig/drivers/pcmcia/rsrc_mgr.c      Sat Nov  2 18:57:58 2002
+++ linux/drivers/pcmcia/rsrc_mgr.c     Wed Nov  6 15:02:10 2002
@@ -55,6 +55,11 @@
 #include <pcmcia/cistpl.h>
 #include "cs_internal.h"

+int cb_alloc_io(ioaddr_t *base, ioaddr_t num, ioaddr_t align, char *name,
+               socket_info_t *s);
+int cb_alloc_mem(unsigned long *base, unsigned long num, unsigned long align,
+                char *name, socket_info_t *s);
+
 /*====================================================================*/

 /* Parameters that can be set with 'insmod' */
@@ -104,34 +109,12 @@

 ======================================================================*/

-static struct resource *resource_parent(unsigned long b, unsigned long n,
-                                       int flags, struct pci_dev *dev)
-{
-#ifdef CONFIG_PCI
-       struct resource res, *pr;
-
-       if (dev != NULL) {
-               res.start = b;
-               res.end = b + n - 1;
-               res.flags = flags;
-               pr = pci_find_parent_resource(dev, &res);
-               if (pr)
-                       return pr;
-       }
-#endif /* CONFIG_PCI */
-       if (flags & IORESOURCE_MEM)
-               return &iomem_resource;
-       return &ioport_resource;
-}
-
 /* FIXME: Fundamentally racy. */
-static inline int check_io_resource(unsigned long b, unsigned long n,
-                                   struct pci_dev *dev)
+static inline int check_io_resource(unsigned long b, unsigned long n)
 {
        struct resource *region;

-       region = __request_region(resource_parent(b, n, IORESOURCE_IO, dev),
-                                 b, n, "check_io_resource");
+       region = __request_region(&ioport_resource, b, n, "check_io_resource");
        if (!region)
                return -EBUSY;

@@ -141,13 +124,12 @@
 }

 /* FIXME: Fundamentally racy. */
-static inline int check_mem_resource(unsigned long b, unsigned long n,
-                                    struct pci_dev *dev)
+static inline int check_mem_resource(unsigned long b, unsigned long n)
 {
        struct resource *region;

-       region = __request_region(resource_parent(b, n, IORESOURCE_MEM, dev),
-                                 b, n, "check_mem_resource");
+       region = __request_region(&iomem_resource, BUS_TO_RESOURCE(b), n,
+                                 "check_mem_resource");
        if (!region)
                return -EBUSY;

@@ -156,49 +138,15 @@
        return 0;
 }

-static struct resource *make_resource(unsigned long b, unsigned long n,
-                                     int flags, char *name)
+static int
+request_mem_resource(unsigned long b, unsigned long n, char *name)
 {
-       struct resource *res = kmalloc(sizeof(*res), GFP_KERNEL);
-
-       if (res) {
-               memset(res, 0, sizeof(*res));
-               res->name = name;
-               res->start = b;
-               res->end = b + n - 1;
-               res->flags = flags | IORESOURCE_BUSY;
-       }
-       return res;
+       return request_mem_region(BUS_TO_RESOURCE(b), n, name) != 0;
 }

-static int request_io_resource(unsigned long b, unsigned long n,
-                              char *name, struct pci_dev *dev)
+void release_mem_resource(unsigned long b, unsigned long n)
 {
-       struct resource *res = make_resource(b, n, IORESOURCE_IO, name);
-       struct resource *pr = resource_parent(b, n, IORESOURCE_IO, dev);
-       int err = -ENOMEM;
-
-       if (res) {
-      
...

read more »

 
 
 

CFT/RFC: New cardbus resource allocation

Post by Russell Kin » Thu, 07 Nov 2002 21:50:10


Gah, sorry - should've mentioned - this patch is against plain 2.5.46.

--

             http://www.arm.linux.org.uk/personal/aboutme.html

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in

More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

 
 
 

CFT/RFC: New cardbus resource allocation

Post by Ivan Kokshaysk » Fri, 08 Nov 2002 12:40:07



> The following patch changes this.  We instead use the PCI resource
> code to allocate a resource of the requested size, alignment, and
> offset from the parent bus of the cardbus bridge.

Looks quite reasonable.
Note that we don't need 2 similar functions in setup-res.c -
pci_assign_resource() can be easily converted to use your
pci_alloc_parent_resource(), and pci_assign_bus_resource() can
be killed then.

Ivan.
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in

More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

 
 
 

CFT/RFC: New cardbus resource allocation

Post by Russell Kin » Fri, 08 Nov 2002 12:40:10



> Looks quite reasonable.
> Note that we don't need 2 similar functions in setup-res.c -
> pci_assign_resource() can be easily converted to use your
> pci_alloc_parent_resource(), and pci_assign_bus_resource() can
> be killed then.

I looked at that and decided it wasn't practical.  pci_assign_resource()
needs the parent resource to pass it down to the architecture specific
layers.  Unfortunately, trying to get that out of
pci_alloc_parent_resource() makes the API rather disgusting IMHO.

--

             http://www.arm.linux.org.uk/personal/aboutme.html

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in

More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

 
 
 

CFT/RFC: New cardbus resource allocation

Post by Ivan Kokshaysk » Tue, 12 Nov 2002 19:10:14




> > Looks quite reasonable.
> > Note that we don't need 2 similar functions in setup-res.c -
> > pci_assign_resource() can be easily converted to use your
> > pci_alloc_parent_resource(), and pci_assign_bus_resource() can
> > be killed then.

> I looked at that and decided it wasn't practical.  pci_assign_resource()
> needs the parent resource to pass it down to the architecture specific
> layers.  Unfortunately, trying to get that out of
> pci_alloc_parent_resource() makes the API rather disgusting IMHO.

Sigh. The API is already broken - passing parent resource to
pcibios_update_resource() is not only pointless (since we have
res->parent), but extremely misleading.
Almost all architectures declare
void
pcibios_update_resource(struct pci_dev *dev, struct resource *root,
                                                              ^^^^
                        struct resource *res, int resource)

Obviously root != parent.
Vast majority of archs do not use `root' arg, but some do:

mips64/mips-boards/generic/pci.c
mips64/sgi-ip27/ip27-pci.c
mips/ite-boards/generic/it8172_pci.c
mips/mips-boards/generic/pci.c
ia64/pci/pci.c

All of these use exactly the same code:

pcibios_update_resource(struct pci_dev *dev, struct resource *root,
                        struct resource *res, int resource)
{
        unsigned long where, size;
        u32 reg;

        where = PCI_BASE_ADDRESS_0 + (resource * 4);
        size = res->end - res->start;
        pci_read_config_dword(dev, where, &reg);
        reg = (reg & size) | (((u32)(res->start - root->start)) & ~size);
        pci_write_config_dword(dev, where, reg);

Quote:}

Which is wrong if the `root' is a pointer to the PCI or CardBus bridge
resource.

The arg 2 to pcibios_update_resource() must be removed...

Ivan.
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in

More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/