1 /* 2 * rsrc_mgr.c -- Resource management routines and/or wrappers 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/module.h> 16 #include <linux/kernel.h> 17 18 #include <pcmcia/cs_types.h> 19 #include <pcmcia/ss.h> 20 #include <pcmcia/cs.h> 21 #include "cs_internal.h" 22 23 24 #ifdef CONFIG_PCMCIA_IOCTL 25 26 #ifdef CONFIG_PCMCIA_PROBE 27 28 static int adjust_irq(struct pcmcia_socket *s, adjust_t *adj) 29 { 30 int irq; 31 u32 mask; 32 33 irq = adj->resource.irq.IRQ; 34 if ((irq < 0) || (irq > 15)) 35 return CS_BAD_IRQ; 36 37 if (adj->Action != REMOVE_MANAGED_RESOURCE) 38 return 0; 39 40 mask = 1 << irq; 41 42 if (!(s->irq_mask & mask)) 43 return 0; 44 45 s->irq_mask &= ~mask; 46 47 return 0; 48 } 49 50 #else 51 52 static inline int adjust_irq(struct pcmcia_socket *s, adjust_t *adj) { 53 return CS_SUCCESS; 54 } 55 56 #endif 57 58 59 int pcmcia_adjust_resource_info(adjust_t *adj) 60 { 61 struct pcmcia_socket *s; 62 int ret = CS_UNSUPPORTED_FUNCTION; 63 unsigned long flags; 64 65 down_read(&pcmcia_socket_list_rwsem); 66 list_for_each_entry(s, &pcmcia_socket_list, socket_list) { 67 68 if (adj->Resource == RES_IRQ) 69 ret = adjust_irq(s, adj); 70 71 else if (s->resource_ops->adjust_resource) { 72 73 /* you can't use the old interface if the new 74 * one was used before */ 75 spin_lock_irqsave(&s->lock, flags); 76 if ((s->resource_setup_new) && 77 !(s->resource_setup_old)) { 78 spin_unlock_irqrestore(&s->lock, flags); 79 continue; 80 } else if (!(s->resource_setup_old)) 81 s->resource_setup_old = 1; 82 spin_unlock_irqrestore(&s->lock, flags); 83 84 ret = s->resource_ops->adjust_resource(s, adj); 85 if (!ret) { 86 /* as there's no way we know this is the 87 * last call to adjust_resource_info, we 88 * always need to assume this is the latest 89 * one... */ 90 spin_lock_irqsave(&s->lock, flags); 91 s->resource_setup_done = 1; 92 spin_unlock_irqrestore(&s->lock, flags); 93 } 94 } 95 } 96 up_read(&pcmcia_socket_list_rwsem); 97 98 return (ret); 99 } 100 EXPORT_SYMBOL(pcmcia_adjust_resource_info); 101 102 #endif 103 104 int pcmcia_validate_mem(struct pcmcia_socket *s) 105 { 106 if (s->resource_ops->validate_mem) 107 return s->resource_ops->validate_mem(s); 108 /* if there is no callback, we can assume that everything is OK */ 109 return 0; 110 } 111 EXPORT_SYMBOL(pcmcia_validate_mem); 112 113 int pcmcia_adjust_io_region(struct resource *res, unsigned long r_start, 114 unsigned long r_end, struct pcmcia_socket *s) 115 { 116 if (s->resource_ops->adjust_io_region) 117 return s->resource_ops->adjust_io_region(res, r_start, r_end, s); 118 return -ENOMEM; 119 } 120 EXPORT_SYMBOL(pcmcia_adjust_io_region); 121 122 struct resource *pcmcia_find_io_region(unsigned long base, int num, 123 unsigned long align, struct pcmcia_socket *s) 124 { 125 if (s->resource_ops->find_io) 126 return s->resource_ops->find_io(base, num, align, s); 127 return NULL; 128 } 129 EXPORT_SYMBOL(pcmcia_find_io_region); 130 131 struct resource *pcmcia_find_mem_region(u_long base, u_long num, u_long align, 132 int low, struct pcmcia_socket *s) 133 { 134 if (s->resource_ops->find_mem) 135 return s->resource_ops->find_mem(base, num, align, low, s); 136 return NULL; 137 } 138 EXPORT_SYMBOL(pcmcia_find_mem_region); 139 140 void release_resource_db(struct pcmcia_socket *s) 141 { 142 if (s->resource_ops->exit) 143 s->resource_ops->exit(s); 144 } 145 146 147 static int static_init(struct pcmcia_socket *s) 148 { 149 unsigned long flags; 150 151 /* the good thing about SS_CAP_STATIC_MAP sockets is 152 * that they don't need a resource database */ 153 154 spin_lock_irqsave(&s->lock, flags); 155 s->resource_setup_done = 1; 156 spin_unlock_irqrestore(&s->lock, flags); 157 158 return 0; 159 } 160 161 162 struct pccard_resource_ops pccard_static_ops = { 163 .validate_mem = NULL, 164 .adjust_io_region = NULL, 165 .find_io = NULL, 166 .find_mem = NULL, 167 .adjust_resource = NULL, 168 .init = static_init, 169 .exit = NULL, 170 }; 171 EXPORT_SYMBOL(pccard_static_ops); 172 173 174 #ifdef CONFIG_PCCARD_IODYN 175 176 static struct resource * 177 make_resource(unsigned long b, unsigned long n, int flags, char *name) 178 { 179 struct resource *res = kzalloc(sizeof(*res), GFP_KERNEL); 180 181 if (res) { 182 res->name = name; 183 res->start = b; 184 res->end = b + n - 1; 185 res->flags = flags; 186 } 187 return res; 188 } 189 190 struct pcmcia_align_data { 191 unsigned long mask; 192 unsigned long offset; 193 }; 194 195 static void pcmcia_align(void *align_data, struct resource *res, 196 unsigned long size, unsigned long align) 197 { 198 struct pcmcia_align_data *data = align_data; 199 unsigned long start; 200 201 start = (res->start & ~data->mask) + data->offset; 202 if (start < res->start) 203 start += data->mask + 1; 204 res->start = start; 205 206 #ifdef CONFIG_X86 207 if (res->flags & IORESOURCE_IO) { 208 if (start & 0x300) { 209 start = (start + 0x3ff) & ~0x3ff; 210 res->start = start; 211 } 212 } 213 #endif 214 215 #ifdef CONFIG_M68K 216 if (res->flags & IORESOURCE_IO) { 217 if ((res->start + size - 1) >= 1024) 218 res->start = res->end; 219 } 220 #endif 221 } 222 223 224 static int iodyn_adjust_io_region(struct resource *res, unsigned long r_start, 225 unsigned long r_end, struct pcmcia_socket *s) 226 { 227 return adjust_resource(res, r_start, r_end - r_start + 1); 228 } 229 230 231 static struct resource *iodyn_find_io_region(unsigned long base, int num, 232 unsigned long align, struct pcmcia_socket *s) 233 { 234 struct resource *res = make_resource(0, num, IORESOURCE_IO, 235 s->dev.bus_id); 236 struct pcmcia_align_data data; 237 unsigned long min = base; 238 int ret; 239 240 if (align == 0) 241 align = 0x10000; 242 243 data.mask = align - 1; 244 data.offset = base & data.mask; 245 246 #ifdef CONFIG_PCI 247 if (s->cb_dev) { 248 ret = pci_bus_alloc_resource(s->cb_dev->bus, res, num, 1, 249 min, 0, pcmcia_align, &data); 250 } else 251 #endif 252 ret = allocate_resource(&ioport_resource, res, num, min, ~0UL, 253 1, pcmcia_align, &data); 254 255 if (ret != 0) { 256 kfree(res); 257 res = NULL; 258 } 259 return res; 260 } 261 262 struct pccard_resource_ops pccard_iodyn_ops = { 263 .validate_mem = NULL, 264 .adjust_io_region = iodyn_adjust_io_region, 265 .find_io = iodyn_find_io_region, 266 .find_mem = NULL, 267 .adjust_resource = NULL, 268 .init = static_init, 269 .exit = NULL, 270 }; 271 EXPORT_SYMBOL(pccard_iodyn_ops); 272 273 #endif /* CONFIG_PCCARD_IODYN */ 274