Merge branch 'for_2.6.35rc' of git://git.pwsan.com/linux-2.6 into omap-fixes-for...
[sfrench/cifs-2.6.git] / drivers / acpi / atomicio.c
1 /*
2  * atomicio.c - ACPI IO memory pre-mapping/post-unmapping, then
3  * accessing in atomic context.
4  *
5  * This is used for NMI handler to access IO memory area, because
6  * ioremap/iounmap can not be used in NMI handler. The IO memory area
7  * is pre-mapped in process context and accessed in NMI handler.
8  *
9  * Copyright (C) 2009-2010, Intel Corp.
10  *      Author: Huang Ying <ying.huang@intel.com>
11  *
12  * This program is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU General Public License version
14  * 2 as published by the Free Software Foundation.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
24  */
25
26 #include <linux/kernel.h>
27 #include <linux/module.h>
28 #include <linux/init.h>
29 #include <linux/acpi.h>
30 #include <linux/io.h>
31 #include <linux/kref.h>
32 #include <linux/rculist.h>
33 #include <linux/interrupt.h>
34 #include <acpi/atomicio.h>
35
36 #define ACPI_PFX "ACPI: "
37
38 static LIST_HEAD(acpi_iomaps);
39 /*
40  * Used for mutual exclusion between writers of acpi_iomaps list, for
41  * synchronization between readers and writer, RCU is used.
42  */
43 static DEFINE_SPINLOCK(acpi_iomaps_lock);
44
45 struct acpi_iomap {
46         struct list_head list;
47         void __iomem *vaddr;
48         unsigned long size;
49         phys_addr_t paddr;
50         struct kref ref;
51 };
52
53 /* acpi_iomaps_lock or RCU read lock must be held before calling */
54 static struct acpi_iomap *__acpi_find_iomap(phys_addr_t paddr,
55                                             unsigned long size)
56 {
57         struct acpi_iomap *map;
58
59         list_for_each_entry_rcu(map, &acpi_iomaps, list) {
60                 if (map->paddr + map->size >= paddr + size &&
61                     map->paddr <= paddr)
62                         return map;
63         }
64         return NULL;
65 }
66
67 /*
68  * Atomic "ioremap" used by NMI handler, if the specified IO memory
69  * area is not pre-mapped, NULL will be returned.
70  *
71  * acpi_iomaps_lock or RCU read lock must be held before calling
72  */
73 static void __iomem *__acpi_ioremap_fast(phys_addr_t paddr,
74                                          unsigned long size)
75 {
76         struct acpi_iomap *map;
77
78         map = __acpi_find_iomap(paddr, size);
79         if (map)
80                 return map->vaddr + (paddr - map->paddr);
81         else
82                 return NULL;
83 }
84
85 /* acpi_iomaps_lock must be held before calling */
86 static void __iomem *__acpi_try_ioremap(phys_addr_t paddr,
87                                         unsigned long size)
88 {
89         struct acpi_iomap *map;
90
91         map = __acpi_find_iomap(paddr, size);
92         if (map) {
93                 kref_get(&map->ref);
94                 return map->vaddr + (paddr - map->paddr);
95         } else
96                 return NULL;
97 }
98
99 /*
100  * Used to pre-map the specified IO memory area. First try to find
101  * whether the area is already pre-mapped, if it is, increase the
102  * reference count (in __acpi_try_ioremap) and return; otherwise, do
103  * the real ioremap, and add the mapping into acpi_iomaps list.
104  */
105 static void __iomem *acpi_pre_map(phys_addr_t paddr,
106                                   unsigned long size)
107 {
108         void __iomem *vaddr;
109         struct acpi_iomap *map;
110         unsigned long pg_sz, flags;
111         phys_addr_t pg_off;
112
113         spin_lock_irqsave(&acpi_iomaps_lock, flags);
114         vaddr = __acpi_try_ioremap(paddr, size);
115         spin_unlock_irqrestore(&acpi_iomaps_lock, flags);
116         if (vaddr)
117                 return vaddr;
118
119         pg_off = paddr & PAGE_MASK;
120         pg_sz = ((paddr + size + PAGE_SIZE - 1) & PAGE_MASK) - pg_off;
121         vaddr = ioremap(pg_off, pg_sz);
122         if (!vaddr)
123                 return NULL;
124         map = kmalloc(sizeof(*map), GFP_KERNEL);
125         if (!map)
126                 goto err_unmap;
127         INIT_LIST_HEAD(&map->list);
128         map->paddr = pg_off;
129         map->size = pg_sz;
130         map->vaddr = vaddr;
131         kref_init(&map->ref);
132
133         spin_lock_irqsave(&acpi_iomaps_lock, flags);
134         vaddr = __acpi_try_ioremap(paddr, size);
135         if (vaddr) {
136                 spin_unlock_irqrestore(&acpi_iomaps_lock, flags);
137                 iounmap(map->vaddr);
138                 kfree(map);
139                 return vaddr;
140         }
141         list_add_tail_rcu(&map->list, &acpi_iomaps);
142         spin_unlock_irqrestore(&acpi_iomaps_lock, flags);
143
144         return vaddr + (paddr - pg_off);
145 err_unmap:
146         iounmap(vaddr);
147         return NULL;
148 }
149
150 /* acpi_iomaps_lock must be held before calling */
151 static void __acpi_kref_del_iomap(struct kref *ref)
152 {
153         struct acpi_iomap *map;
154
155         map = container_of(ref, struct acpi_iomap, ref);
156         list_del_rcu(&map->list);
157 }
158
159 /*
160  * Used to post-unmap the specified IO memory area. The iounmap is
161  * done only if the reference count goes zero.
162  */
163 static void acpi_post_unmap(phys_addr_t paddr, unsigned long size)
164 {
165         struct acpi_iomap *map;
166         unsigned long flags;
167         int del;
168
169         spin_lock_irqsave(&acpi_iomaps_lock, flags);
170         map = __acpi_find_iomap(paddr, size);
171         BUG_ON(!map);
172         del = kref_put(&map->ref, __acpi_kref_del_iomap);
173         spin_unlock_irqrestore(&acpi_iomaps_lock, flags);
174
175         if (!del)
176                 return;
177
178         synchronize_rcu();
179         iounmap(map->vaddr);
180         kfree(map);
181 }
182
183 /* In NMI handler, should set silent = 1 */
184 static int acpi_check_gar(struct acpi_generic_address *reg,
185                           u64 *paddr, int silent)
186 {
187         u32 width, space_id;
188
189         width = reg->bit_width;
190         space_id = reg->space_id;
191         /* Handle possible alignment issues */
192         memcpy(paddr, &reg->address, sizeof(*paddr));
193         if (!*paddr) {
194                 if (!silent)
195                         pr_warning(FW_BUG ACPI_PFX
196                         "Invalid physical address in GAR [0x%llx/%u/%u]\n",
197                                    *paddr, width, space_id);
198                 return -EINVAL;
199         }
200
201         if ((width != 8) && (width != 16) && (width != 32) && (width != 64)) {
202                 if (!silent)
203                         pr_warning(FW_BUG ACPI_PFX
204                                    "Invalid bit width in GAR [0x%llx/%u/%u]\n",
205                                    *paddr, width, space_id);
206                 return -EINVAL;
207         }
208
209         if (space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY &&
210             space_id != ACPI_ADR_SPACE_SYSTEM_IO) {
211                 if (!silent)
212                         pr_warning(FW_BUG ACPI_PFX
213                         "Invalid address space type in GAR [0x%llx/%u/%u]\n",
214                                    *paddr, width, space_id);
215                 return -EINVAL;
216         }
217
218         return 0;
219 }
220
221 /* Pre-map, working on GAR */
222 int acpi_pre_map_gar(struct acpi_generic_address *reg)
223 {
224         u64 paddr;
225         void __iomem *vaddr;
226         int rc;
227
228         if (reg->space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY)
229                 return 0;
230
231         rc = acpi_check_gar(reg, &paddr, 0);
232         if (rc)
233                 return rc;
234
235         vaddr = acpi_pre_map(paddr, reg->bit_width / 8);
236         if (!vaddr)
237                 return -EIO;
238
239         return 0;
240 }
241 EXPORT_SYMBOL_GPL(acpi_pre_map_gar);
242
243 /* Post-unmap, working on GAR */
244 int acpi_post_unmap_gar(struct acpi_generic_address *reg)
245 {
246         u64 paddr;
247         int rc;
248
249         if (reg->space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY)
250                 return 0;
251
252         rc = acpi_check_gar(reg, &paddr, 0);
253         if (rc)
254                 return rc;
255
256         acpi_post_unmap(paddr, reg->bit_width / 8);
257
258         return 0;
259 }
260 EXPORT_SYMBOL_GPL(acpi_post_unmap_gar);
261
262 /*
263  * Can be used in atomic (including NMI) or process context. RCU read
264  * lock can only be released after the IO memory area accessing.
265  */
266 static int acpi_atomic_read_mem(u64 paddr, u64 *val, u32 width)
267 {
268         void __iomem *addr;
269
270         rcu_read_lock();
271         addr = __acpi_ioremap_fast(paddr, width);
272         switch (width) {
273         case 8:
274                 *val = readb(addr);
275                 break;
276         case 16:
277                 *val = readw(addr);
278                 break;
279         case 32:
280                 *val = readl(addr);
281                 break;
282         case 64:
283                 *val = readq(addr);
284                 break;
285         default:
286                 return -EINVAL;
287         }
288         rcu_read_unlock();
289
290         return 0;
291 }
292
293 static int acpi_atomic_write_mem(u64 paddr, u64 val, u32 width)
294 {
295         void __iomem *addr;
296
297         rcu_read_lock();
298         addr = __acpi_ioremap_fast(paddr, width);
299         switch (width) {
300         case 8:
301                 writeb(val, addr);
302                 break;
303         case 16:
304                 writew(val, addr);
305                 break;
306         case 32:
307                 writel(val, addr);
308                 break;
309         case 64:
310                 writeq(val, addr);
311                 break;
312         default:
313                 return -EINVAL;
314         }
315         rcu_read_unlock();
316
317         return 0;
318 }
319
320 /* GAR accessing in atomic (including NMI) or process context */
321 int acpi_atomic_read(u64 *val, struct acpi_generic_address *reg)
322 {
323         u64 paddr;
324         int rc;
325
326         rc = acpi_check_gar(reg, &paddr, 1);
327         if (rc)
328                 return rc;
329
330         *val = 0;
331         switch (reg->space_id) {
332         case ACPI_ADR_SPACE_SYSTEM_MEMORY:
333                 return acpi_atomic_read_mem(paddr, val, reg->bit_width);
334         case ACPI_ADR_SPACE_SYSTEM_IO:
335                 return acpi_os_read_port(paddr, (u32 *)val, reg->bit_width);
336         default:
337                 return -EINVAL;
338         }
339 }
340 EXPORT_SYMBOL_GPL(acpi_atomic_read);
341
342 int acpi_atomic_write(u64 val, struct acpi_generic_address *reg)
343 {
344         u64 paddr;
345         int rc;
346
347         rc = acpi_check_gar(reg, &paddr, 1);
348         if (rc)
349                 return rc;
350
351         switch (reg->space_id) {
352         case ACPI_ADR_SPACE_SYSTEM_MEMORY:
353                 return acpi_atomic_write_mem(paddr, val, reg->bit_width);
354         case ACPI_ADR_SPACE_SYSTEM_IO:
355                 return acpi_os_write_port(paddr, val, reg->bit_width);
356         default:
357                 return -EINVAL;
358         }
359 }
360 EXPORT_SYMBOL_GPL(acpi_atomic_write);