Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * rsrc_iodyn.c -- Resource management routines for MEM-static sockets.
0004  *
0005  * The initial developer of the original code is David A. Hinds
0006  * <dahinds@users.sourceforge.net>.  Portions created by David A. Hinds
0007  * are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
0008  *
0009  * (C) 1999     David A. Hinds
0010  */
0011 
0012 #include <linux/slab.h>
0013 #include <linux/module.h>
0014 #include <linux/kernel.h>
0015 
0016 #include <pcmcia/ss.h>
0017 #include <pcmcia/cistpl.h>
0018 #include "cs_internal.h"
0019 
0020 
0021 struct pcmcia_align_data {
0022     unsigned long   mask;
0023     unsigned long   offset;
0024 };
0025 
0026 static resource_size_t pcmcia_align(void *align_data,
0027                 const struct resource *res,
0028                 resource_size_t size, resource_size_t align)
0029 {
0030     struct pcmcia_align_data *data = align_data;
0031     resource_size_t start;
0032 
0033     start = (res->start & ~data->mask) + data->offset;
0034     if (start < res->start)
0035         start += data->mask + 1;
0036 
0037 #ifdef CONFIG_X86
0038     if (res->flags & IORESOURCE_IO) {
0039         if (start & 0x300)
0040             start = (start + 0x3ff) & ~0x3ff;
0041     }
0042 #endif
0043 
0044 #ifdef CONFIG_M68K
0045     if (res->flags & IORESOURCE_IO) {
0046         if ((res->start + size - 1) >= 1024)
0047             start = res->end;
0048     }
0049 #endif
0050 
0051     return start;
0052 }
0053 
0054 
0055 static struct resource *__iodyn_find_io_region(struct pcmcia_socket *s,
0056                     unsigned long base, int num,
0057                     unsigned long align)
0058 {
0059     struct resource *res = pcmcia_make_resource(0, num, IORESOURCE_IO,
0060                         dev_name(&s->dev));
0061     struct pcmcia_align_data data;
0062     unsigned long min = base;
0063     int ret;
0064 
0065     data.mask = align - 1;
0066     data.offset = base & data.mask;
0067 
0068 #ifdef CONFIG_PCI
0069     if (s->cb_dev) {
0070         ret = pci_bus_alloc_resource(s->cb_dev->bus, res, num, 1,
0071                          min, 0, pcmcia_align, &data);
0072     } else
0073 #endif
0074         ret = allocate_resource(&ioport_resource, res, num, min, ~0UL,
0075                     1, pcmcia_align, &data);
0076 
0077     if (ret != 0) {
0078         kfree(res);
0079         res = NULL;
0080     }
0081     return res;
0082 }
0083 
0084 static int iodyn_find_io(struct pcmcia_socket *s, unsigned int attr,
0085             unsigned int *base, unsigned int num,
0086             unsigned int align, struct resource **parent)
0087 {
0088     int i, ret = 0;
0089 
0090     /* Check for an already-allocated window that must conflict with
0091      * what was asked for.  It is a hack because it does not catch all
0092      * potential conflicts, just the most obvious ones.
0093      */
0094     for (i = 0; i < MAX_IO_WIN; i++) {
0095         if (!s->io[i].res)
0096             continue;
0097 
0098         if (!*base)
0099             continue;
0100 
0101         if ((s->io[i].res->start & (align-1)) == *base)
0102             return -EBUSY;
0103     }
0104 
0105     for (i = 0; i < MAX_IO_WIN; i++) {
0106         struct resource *res = s->io[i].res;
0107         unsigned int try;
0108 
0109         if (res && (res->flags & IORESOURCE_BITS) !=
0110             (attr & IORESOURCE_BITS))
0111             continue;
0112 
0113         if (!res) {
0114             if (align == 0)
0115                 align = 0x10000;
0116 
0117             res = s->io[i].res = __iodyn_find_io_region(s, *base,
0118                                 num, align);
0119             if (!res)
0120                 return -EINVAL;
0121 
0122             *base = res->start;
0123             s->io[i].res->flags =
0124                 ((res->flags & ~IORESOURCE_BITS) |
0125                     (attr & IORESOURCE_BITS));
0126             s->io[i].InUse = num;
0127             *parent = res;
0128             return 0;
0129         }
0130 
0131         /* Try to extend top of window */
0132         try = res->end + 1;
0133         if ((*base == 0) || (*base == try)) {
0134             if (adjust_resource(s->io[i].res, res->start,
0135                         resource_size(res) + num))
0136                 continue;
0137             *base = try;
0138             s->io[i].InUse += num;
0139             *parent = res;
0140             return 0;
0141         }
0142 
0143         /* Try to extend bottom of window */
0144         try = res->start - num;
0145         if ((*base == 0) || (*base == try)) {
0146             if (adjust_resource(s->io[i].res,
0147                         res->start - num,
0148                         resource_size(res) + num))
0149                 continue;
0150             *base = try;
0151             s->io[i].InUse += num;
0152             *parent = res;
0153             return 0;
0154         }
0155     }
0156 
0157     return -EINVAL;
0158 }
0159 
0160 
0161 struct pccard_resource_ops pccard_iodyn_ops = {
0162     .validate_mem = NULL,
0163     .find_io = iodyn_find_io,
0164     .find_mem = NULL,
0165     .init = static_init,
0166     .exit = NULL,
0167 };
0168 EXPORT_SYMBOL(pccard_iodyn_ops);