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