d5764a3d74af2b73176444212e21f7747b35f679
[linux-2.6.git] / arch / ia64 / kernel / esi.c
1 /*
2  * Extensible SAL Interface (ESI) support routines.
3  *
4  * Copyright (C) 2006 Hewlett-Packard Co
5  *      Alex Williamson <alex.williamson@hp.com>
6  */
7 #include <linux/kernel.h>
8 #include <linux/init.h>
9 #include <linux/module.h>
10 #include <linux/string.h>
11
12 #include <asm/esi.h>
13 #include <asm/sal.h>
14
15 MODULE_AUTHOR("Alex Williamson <alex.williamson@hp.com>");
16 MODULE_DESCRIPTION("Extensible SAL Interface (ESI) support");
17 MODULE_LICENSE("GPL");
18
19 #define MODULE_NAME     "esi"
20
21 #define ESI_TABLE_GUID                                  \
22     EFI_GUID(0x43EA58DC, 0xCF28, 0x4b06, 0xB3,          \
23              0x91, 0xB7, 0x50, 0x59, 0x34, 0x2B, 0xD4)
24
25 enum esi_systab_entry_type {
26         ESI_DESC_ENTRY_POINT = 0
27 };
28
29 /*
30  * Entry type:  Size:
31  *      0       48
32  */
33 #define ESI_DESC_SIZE(type)     "\060"[(unsigned) (type)]
34
35 typedef struct ia64_esi_desc_entry_point {
36         u8 type;
37         u8 reserved1[15];
38         u64 esi_proc;
39         u64 gp;
40         efi_guid_t guid;
41 } ia64_esi_desc_entry_point_t;
42
43 struct pdesc {
44         void *addr;
45         void *gp;
46 };
47
48 static struct ia64_sal_systab *esi_systab;
49
50 static int __init esi_init (void)
51 {
52         efi_config_table_t *config_tables;
53         struct ia64_sal_systab *systab;
54         unsigned long esi = 0;
55         char *p;
56         int i;
57
58         config_tables = __va(efi.systab->tables);
59
60         for (i = 0; i < (int) efi.systab->nr_tables; ++i) {
61                 if (efi_guidcmp(config_tables[i].guid, ESI_TABLE_GUID) == 0) {
62                         esi = config_tables[i].table;
63                         break;
64                 }
65         }
66
67         if (!esi)
68                 return -ENODEV;
69
70         systab = __va(esi);
71
72         if (strncmp(systab->signature, "ESIT", 4) != 0) {
73                 printk(KERN_ERR "bad signature in ESI system table!");
74                 return -ENODEV;
75         }
76
77         p = (char *) (systab + 1);
78         for (i = 0; i < systab->entry_count; i++) {
79                 /*
80                  * The first byte of each entry type contains the type
81                  * descriptor.
82                  */
83                 switch (*p) {
84                       case ESI_DESC_ENTRY_POINT:
85                         break;
86                       default:
87                         printk(KERN_WARNING "Unkown table type %d found in "
88                                "ESI table, ignoring rest of table\n", *p);
89                         return -ENODEV;
90                 }
91
92                 p += ESI_DESC_SIZE(*p);
93         }
94
95         esi_systab = systab;
96         return 0;
97 }
98
99
100 int ia64_esi_call (efi_guid_t guid, struct ia64_sal_retval *isrvp,
101                    enum esi_proc_type proc_type, u64 func,
102                    u64 arg1, u64 arg2, u64 arg3, u64 arg4, u64 arg5, u64 arg6,
103                    u64 arg7)
104 {
105         struct ia64_fpreg fr[6];
106         unsigned long flags = 0;
107         int i;
108         char *p;
109
110         if (!esi_systab)
111                 return -1;
112
113         p = (char *) (esi_systab + 1);
114         for (i = 0; i < esi_systab->entry_count; i++) {
115                 if (*p == ESI_DESC_ENTRY_POINT) {
116                         ia64_esi_desc_entry_point_t *esi = (void *)p;
117                         if (!efi_guidcmp(guid, esi->guid)) {
118                                 ia64_sal_handler esi_proc;
119                                 struct pdesc pdesc;
120
121                                 pdesc.addr = __va(esi->esi_proc);
122                                 pdesc.gp = __va(esi->gp);
123
124                                 esi_proc = (ia64_sal_handler) &pdesc;
125
126                                 ia64_save_scratch_fpregs(fr);
127                                 if (proc_type == ESI_PROC_SERIALIZED)
128                                         spin_lock_irqsave(&sal_lock, flags);
129                                 else if (proc_type == ESI_PROC_MP_SAFE)
130                                         local_irq_save(flags);
131                                 else
132                                         preempt_disable();
133                                 *isrvp = (*esi_proc)(func, arg1, arg2, arg3,
134                                                      arg4, arg5, arg6, arg7);
135                                 if (proc_type == ESI_PROC_SERIALIZED)
136                                         spin_unlock_irqrestore(&sal_lock,
137                                                                flags);
138                                 else if (proc_type == ESI_PROC_MP_SAFE)
139                                         local_irq_restore(flags);
140                                 else
141                                         preempt_enable();
142                                 ia64_load_scratch_fpregs(fr);
143                                 return 0;
144                         }
145                 }
146                 p += ESI_DESC_SIZE(*p);
147         }
148         return -1;
149 }
150 EXPORT_SYMBOL_GPL(ia64_esi_call);
151
152 int ia64_esi_call_phys (efi_guid_t guid, struct ia64_sal_retval *isrvp,
153                         u64 func, u64 arg1, u64 arg2, u64 arg3, u64 arg4,
154                         u64 arg5, u64 arg6, u64 arg7)
155 {
156         struct ia64_fpreg fr[6];
157         unsigned long flags;
158         u64 esi_params[8];
159         char *p;
160         int i;
161
162         if (!esi_systab)
163                 return -1;
164
165         p = (char *) (esi_systab + 1);
166         for (i = 0; i < esi_systab->entry_count; i++) {
167                 if (*p == ESI_DESC_ENTRY_POINT) {
168                         ia64_esi_desc_entry_point_t *esi = (void *)p;
169                         if (!efi_guidcmp(guid, esi->guid)) {
170                                 ia64_sal_handler esi_proc;
171                                 struct pdesc pdesc;
172
173                                 pdesc.addr = (void *)esi->esi_proc;
174                                 pdesc.gp = (void *)esi->gp;
175
176                                 esi_proc = (ia64_sal_handler) &pdesc;
177
178                                 esi_params[0] = func;
179                                 esi_params[1] = arg1;
180                                 esi_params[2] = arg2;
181                                 esi_params[3] = arg3;
182                                 esi_params[4] = arg4;
183                                 esi_params[5] = arg5;
184                                 esi_params[6] = arg6;
185                                 esi_params[7] = arg7;
186                                 ia64_save_scratch_fpregs(fr);
187                                 spin_lock_irqsave(&sal_lock, flags);
188                                 *isrvp = esi_call_phys(esi_proc, esi_params);
189                                 spin_unlock_irqrestore(&sal_lock, flags);
190                                 ia64_load_scratch_fpregs(fr);
191                                 return 0;
192                         }
193                 }
194                 p += ESI_DESC_SIZE(*p);
195         }
196         return -1;
197 }
198 EXPORT_SYMBOL_GPL(ia64_esi_call_phys);
199
200 static void __exit esi_exit (void)
201 {
202 }
203
204 module_init(esi_init);
205 module_exit(esi_exit);  /* makes module removable... */