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
15MODULE_AUTHOR("Alex Williamson <alex.williamson@hp.com>");
16MODULE_DESCRIPTION("Extensible SAL Interface (ESI) support");
17MODULE_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
25enum 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
35typedef 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
43struct pdesc {
44	void *addr;
45	void *gp;
46};
47
48static struct ia64_sal_systab *esi_systab;
49
50static 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 "Unknown 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
100int 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}
150EXPORT_SYMBOL_GPL(ia64_esi_call);
151
152int 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}
198EXPORT_SYMBOL_GPL(ia64_esi_call_phys);
199
200static void __exit esi_exit (void)
201{
202}
203
204module_init(esi_init);
205module_exit(esi_exit);	/* makes module removable... */
206