Commit | Line | Data |
---|---|---|
1da177e4 LT |
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 | ||
1da177e4 LT |
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 | ||
0e0fad8f DB |
24 | #ifdef CONFIG_PCMCIA_IOCTL |
25 | ||
1da177e4 LT |
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); | |
3c29976a | 76 | if ((s->resource_setup_new) && |
1da177e4 LT |
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 | ||
0e0fad8f DB |
102 | #endif |
103 | ||
de75914e | 104 | int pcmcia_validate_mem(struct pcmcia_socket *s) |
1da177e4 LT |
105 | { |
106 | if (s->resource_ops->validate_mem) | |
de75914e DB |
107 | return s->resource_ops->validate_mem(s); |
108 | /* if there is no callback, we can assume that everything is OK */ | |
109 | return 0; | |
1da177e4 LT |
110 | } |
111 | EXPORT_SYMBOL(pcmcia_validate_mem); | |
112 | ||
e6ea0b9e | 113 | int pcmcia_adjust_io_region(struct resource *res, unsigned long r_start, |
1da177e4 LT |
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 | } | |
1a8d4663 | 120 | EXPORT_SYMBOL(pcmcia_adjust_io_region); |
1da177e4 | 121 | |
e6ea0b9e | 122 | struct resource *pcmcia_find_io_region(unsigned long base, int num, |
1da177e4 LT |
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 | } | |
1a8d4663 | 129 | EXPORT_SYMBOL(pcmcia_find_io_region); |
1da177e4 | 130 | |
e6ea0b9e | 131 | struct resource *pcmcia_find_mem_region(u_long base, u_long num, u_long align, |
1da177e4 LT |
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 | } | |
1a8d4663 | 138 | EXPORT_SYMBOL(pcmcia_find_mem_region); |
1da177e4 LT |
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); | |
3b27e942 DB |
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, | |
dfe461ae | 235 | s->dev.bus_id); |
3b27e942 DB |
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 */ |