^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0-only
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * rsrc_iodyn.c -- Resource management routines for MEM-static sockets.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) * The initial developer of the original code is David A. Hinds
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) * <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) * are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) * (C) 1999 David A. Hinds
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) #include <linux/slab.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) #include <linux/module.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) #include <linux/kernel.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) #include <pcmcia/ss.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) #include <pcmcia/cistpl.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) #include "cs_internal.h"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) struct pcmcia_align_data {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) unsigned long mask;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) unsigned long offset;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) static resource_size_t pcmcia_align(void *align_data,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) const struct resource *res,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) resource_size_t size, resource_size_t align)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) struct pcmcia_align_data *data = align_data;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) resource_size_t start;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) start = (res->start & ~data->mask) + data->offset;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) if (start < res->start)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) start += data->mask + 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) #ifdef CONFIG_X86
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) if (res->flags & IORESOURCE_IO) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) if (start & 0x300)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) start = (start + 0x3ff) & ~0x3ff;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) #ifdef CONFIG_M68K
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) if (res->flags & IORESOURCE_IO) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) if ((res->start + size - 1) >= 1024)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) start = res->end;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) return start;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) static struct resource *__iodyn_find_io_region(struct pcmcia_socket *s,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) unsigned long base, int num,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) unsigned long align)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) struct resource *res = pcmcia_make_resource(0, num, IORESOURCE_IO,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) dev_name(&s->dev));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) struct pcmcia_align_data data;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) unsigned long min = base;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) data.mask = align - 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) data.offset = base & data.mask;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) #ifdef CONFIG_PCI
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) if (s->cb_dev) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) ret = pci_bus_alloc_resource(s->cb_dev->bus, res, num, 1,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) min, 0, pcmcia_align, &data);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) } else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) ret = allocate_resource(&ioport_resource, res, num, min, ~0UL,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) 1, pcmcia_align, &data);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) if (ret != 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) kfree(res);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) res = NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) return res;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) static int iodyn_find_io(struct pcmcia_socket *s, unsigned int attr,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) unsigned int *base, unsigned int num,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) unsigned int align, struct resource **parent)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) int i, ret = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) /* Check for an already-allocated window that must conflict with
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) * what was asked for. It is a hack because it does not catch all
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) * potential conflicts, just the most obvious ones.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) for (i = 0; i < MAX_IO_WIN; i++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) if (!s->io[i].res)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) continue;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) if (!*base)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) continue;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) if ((s->io[i].res->start & (align-1)) == *base)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) return -EBUSY;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) for (i = 0; i < MAX_IO_WIN; i++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) struct resource *res = s->io[i].res;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) unsigned int try;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) if (res && (res->flags & IORESOURCE_BITS) !=
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) (attr & IORESOURCE_BITS))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) continue;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) if (!res) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) if (align == 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) align = 0x10000;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) res = s->io[i].res = __iodyn_find_io_region(s, *base,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) num, align);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) if (!res)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) *base = res->start;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) s->io[i].res->flags =
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) ((res->flags & ~IORESOURCE_BITS) |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) (attr & IORESOURCE_BITS));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) s->io[i].InUse = num;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) *parent = res;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) /* Try to extend top of window */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) try = res->end + 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) if ((*base == 0) || (*base == try)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) if (adjust_resource(s->io[i].res, res->start,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) resource_size(res) + num))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) continue;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) *base = try;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) s->io[i].InUse += num;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) *parent = res;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) /* Try to extend bottom of window */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) try = res->start - num;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) if ((*base == 0) || (*base == try)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) if (adjust_resource(s->io[i].res,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) res->start - num,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) resource_size(res) + num))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) continue;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) *base = try;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) s->io[i].InUse += num;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) *parent = res;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161) struct pccard_resource_ops pccard_iodyn_ops = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162) .validate_mem = NULL,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163) .find_io = iodyn_find_io,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164) .find_mem = NULL,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165) .init = static_init,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166) .exit = NULL,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168) EXPORT_SYMBOL(pccard_iodyn_ops);