xref: /openbmc/linux/arch/ia64/kernel/esi.c (revision 09c434b8)
109c434b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
22ab561a1SDavid Mosberger-Tang /*
32ab561a1SDavid Mosberger-Tang  * Extensible SAL Interface (ESI) support routines.
42ab561a1SDavid Mosberger-Tang  *
52ab561a1SDavid Mosberger-Tang  * Copyright (C) 2006 Hewlett-Packard Co
62ab561a1SDavid Mosberger-Tang  * 	Alex Williamson <alex.williamson@hp.com>
72ab561a1SDavid Mosberger-Tang  */
82ab561a1SDavid Mosberger-Tang #include <linux/kernel.h>
92ab561a1SDavid Mosberger-Tang #include <linux/init.h>
102ab561a1SDavid Mosberger-Tang #include <linux/module.h>
112ab561a1SDavid Mosberger-Tang #include <linux/string.h>
122ab561a1SDavid Mosberger-Tang 
132ab561a1SDavid Mosberger-Tang #include <asm/esi.h>
142ab561a1SDavid Mosberger-Tang #include <asm/sal.h>
152ab561a1SDavid Mosberger-Tang 
162ab561a1SDavid Mosberger-Tang MODULE_AUTHOR("Alex Williamson <alex.williamson@hp.com>");
172ab561a1SDavid Mosberger-Tang MODULE_DESCRIPTION("Extensible SAL Interface (ESI) support");
182ab561a1SDavid Mosberger-Tang MODULE_LICENSE("GPL");
192ab561a1SDavid Mosberger-Tang 
202ab561a1SDavid Mosberger-Tang #define MODULE_NAME	"esi"
212ab561a1SDavid Mosberger-Tang 
222ab561a1SDavid Mosberger-Tang #define ESI_TABLE_GUID					\
232ab561a1SDavid Mosberger-Tang     EFI_GUID(0x43EA58DC, 0xCF28, 0x4b06, 0xB3,		\
242ab561a1SDavid Mosberger-Tang 	     0x91, 0xB7, 0x50, 0x59, 0x34, 0x2B, 0xD4)
252ab561a1SDavid Mosberger-Tang 
262ab561a1SDavid Mosberger-Tang enum esi_systab_entry_type {
272ab561a1SDavid Mosberger-Tang 	ESI_DESC_ENTRY_POINT = 0
282ab561a1SDavid Mosberger-Tang };
292ab561a1SDavid Mosberger-Tang 
302ab561a1SDavid Mosberger-Tang /*
312ab561a1SDavid Mosberger-Tang  * Entry type:	Size:
322ab561a1SDavid Mosberger-Tang  *	0	48
332ab561a1SDavid Mosberger-Tang  */
342ab561a1SDavid Mosberger-Tang #define ESI_DESC_SIZE(type)	"\060"[(unsigned) (type)]
352ab561a1SDavid Mosberger-Tang 
362ab561a1SDavid Mosberger-Tang typedef struct ia64_esi_desc_entry_point {
372ab561a1SDavid Mosberger-Tang 	u8 type;
382ab561a1SDavid Mosberger-Tang 	u8 reserved1[15];
392ab561a1SDavid Mosberger-Tang 	u64 esi_proc;
402ab561a1SDavid Mosberger-Tang 	u64 gp;
412ab561a1SDavid Mosberger-Tang 	efi_guid_t guid;
422ab561a1SDavid Mosberger-Tang } ia64_esi_desc_entry_point_t;
432ab561a1SDavid Mosberger-Tang 
442ab561a1SDavid Mosberger-Tang struct pdesc {
452ab561a1SDavid Mosberger-Tang 	void *addr;
462ab561a1SDavid Mosberger-Tang 	void *gp;
472ab561a1SDavid Mosberger-Tang };
482ab561a1SDavid Mosberger-Tang 
492ab561a1SDavid Mosberger-Tang static struct ia64_sal_systab *esi_systab;
502ab561a1SDavid Mosberger-Tang 
512ab561a1SDavid Mosberger-Tang static int __init esi_init (void)
522ab561a1SDavid Mosberger-Tang {
532ab561a1SDavid Mosberger-Tang 	efi_config_table_t *config_tables;
542ab561a1SDavid Mosberger-Tang 	struct ia64_sal_systab *systab;
552ab561a1SDavid Mosberger-Tang 	unsigned long esi = 0;
562ab561a1SDavid Mosberger-Tang 	char *p;
572ab561a1SDavid Mosberger-Tang 	int i;
582ab561a1SDavid Mosberger-Tang 
592ab561a1SDavid Mosberger-Tang 	config_tables = __va(efi.systab->tables);
602ab561a1SDavid Mosberger-Tang 
612ab561a1SDavid Mosberger-Tang 	for (i = 0; i < (int) efi.systab->nr_tables; ++i) {
622ab561a1SDavid Mosberger-Tang 		if (efi_guidcmp(config_tables[i].guid, ESI_TABLE_GUID) == 0) {
632ab561a1SDavid Mosberger-Tang 			esi = config_tables[i].table;
642ab561a1SDavid Mosberger-Tang 			break;
652ab561a1SDavid Mosberger-Tang 		}
662ab561a1SDavid Mosberger-Tang 	}
672ab561a1SDavid Mosberger-Tang 
682ab561a1SDavid Mosberger-Tang 	if (!esi)
6958782b34SJoe Perches 		return -ENODEV;
702ab561a1SDavid Mosberger-Tang 
712ab561a1SDavid Mosberger-Tang 	systab = __va(esi);
722ab561a1SDavid Mosberger-Tang 
732ab561a1SDavid Mosberger-Tang 	if (strncmp(systab->signature, "ESIT", 4) != 0) {
742ab561a1SDavid Mosberger-Tang 		printk(KERN_ERR "bad signature in ESI system table!");
752ab561a1SDavid Mosberger-Tang 		return -ENODEV;
762ab561a1SDavid Mosberger-Tang 	}
772ab561a1SDavid Mosberger-Tang 
782ab561a1SDavid Mosberger-Tang 	p = (char *) (systab + 1);
792ab561a1SDavid Mosberger-Tang 	for (i = 0; i < systab->entry_count; i++) {
802ab561a1SDavid Mosberger-Tang 		/*
812ab561a1SDavid Mosberger-Tang 		 * The first byte of each entry type contains the type
822ab561a1SDavid Mosberger-Tang 		 * descriptor.
832ab561a1SDavid Mosberger-Tang 		 */
842ab561a1SDavid Mosberger-Tang 		switch (*p) {
852ab561a1SDavid Mosberger-Tang 		      case ESI_DESC_ENTRY_POINT:
862ab561a1SDavid Mosberger-Tang 			break;
872ab561a1SDavid Mosberger-Tang 		      default:
88af901ca1SAndré Goddard Rosa 			printk(KERN_WARNING "Unknown table type %d found in "
892ab561a1SDavid Mosberger-Tang 			       "ESI table, ignoring rest of table\n", *p);
902ab561a1SDavid Mosberger-Tang 			return -ENODEV;
912ab561a1SDavid Mosberger-Tang 		}
922ab561a1SDavid Mosberger-Tang 
932ab561a1SDavid Mosberger-Tang 		p += ESI_DESC_SIZE(*p);
942ab561a1SDavid Mosberger-Tang 	}
952ab561a1SDavid Mosberger-Tang 
962ab561a1SDavid Mosberger-Tang 	esi_systab = systab;
972ab561a1SDavid Mosberger-Tang 	return 0;
982ab561a1SDavid Mosberger-Tang }
992ab561a1SDavid Mosberger-Tang 
1002ab561a1SDavid Mosberger-Tang 
1012ab561a1SDavid Mosberger-Tang int ia64_esi_call (efi_guid_t guid, struct ia64_sal_retval *isrvp,
1022ab561a1SDavid Mosberger-Tang 		   enum esi_proc_type proc_type, u64 func,
1032ab561a1SDavid Mosberger-Tang 		   u64 arg1, u64 arg2, u64 arg3, u64 arg4, u64 arg5, u64 arg6,
1042ab561a1SDavid Mosberger-Tang 		   u64 arg7)
1052ab561a1SDavid Mosberger-Tang {
1062ab561a1SDavid Mosberger-Tang 	struct ia64_fpreg fr[6];
1072ab561a1SDavid Mosberger-Tang 	unsigned long flags = 0;
1082ab561a1SDavid Mosberger-Tang 	int i;
1092ab561a1SDavid Mosberger-Tang 	char *p;
1102ab561a1SDavid Mosberger-Tang 
1112ab561a1SDavid Mosberger-Tang 	if (!esi_systab)
1122ab561a1SDavid Mosberger-Tang 		return -1;
1132ab561a1SDavid Mosberger-Tang 
1142ab561a1SDavid Mosberger-Tang 	p = (char *) (esi_systab + 1);
1152ab561a1SDavid Mosberger-Tang 	for (i = 0; i < esi_systab->entry_count; i++) {
1162ab561a1SDavid Mosberger-Tang 		if (*p == ESI_DESC_ENTRY_POINT) {
1172ab561a1SDavid Mosberger-Tang 			ia64_esi_desc_entry_point_t *esi = (void *)p;
1182ab561a1SDavid Mosberger-Tang 			if (!efi_guidcmp(guid, esi->guid)) {
1192ab561a1SDavid Mosberger-Tang 				ia64_sal_handler esi_proc;
1202ab561a1SDavid Mosberger-Tang 				struct pdesc pdesc;
1212ab561a1SDavid Mosberger-Tang 
1222ab561a1SDavid Mosberger-Tang 				pdesc.addr = __va(esi->esi_proc);
1232ab561a1SDavid Mosberger-Tang 				pdesc.gp = __va(esi->gp);
1242ab561a1SDavid Mosberger-Tang 
1252ab561a1SDavid Mosberger-Tang 				esi_proc = (ia64_sal_handler) &pdesc;
1262ab561a1SDavid Mosberger-Tang 
1272ab561a1SDavid Mosberger-Tang 				ia64_save_scratch_fpregs(fr);
1282ab561a1SDavid Mosberger-Tang 				if (proc_type == ESI_PROC_SERIALIZED)
1292ab561a1SDavid Mosberger-Tang 					spin_lock_irqsave(&sal_lock, flags);
1302ab561a1SDavid Mosberger-Tang 				else if (proc_type == ESI_PROC_MP_SAFE)
1312ab561a1SDavid Mosberger-Tang 					local_irq_save(flags);
1322ab561a1SDavid Mosberger-Tang 				else
1332ab561a1SDavid Mosberger-Tang 					preempt_disable();
1342ab561a1SDavid Mosberger-Tang 				*isrvp = (*esi_proc)(func, arg1, arg2, arg3,
1352ab561a1SDavid Mosberger-Tang 						     arg4, arg5, arg6, arg7);
1362ab561a1SDavid Mosberger-Tang 				if (proc_type == ESI_PROC_SERIALIZED)
1372ab561a1SDavid Mosberger-Tang 					spin_unlock_irqrestore(&sal_lock,
1382ab561a1SDavid Mosberger-Tang 							       flags);
1392ab561a1SDavid Mosberger-Tang 				else if (proc_type == ESI_PROC_MP_SAFE)
1402ab561a1SDavid Mosberger-Tang 					local_irq_restore(flags);
1412ab561a1SDavid Mosberger-Tang 				else
1422ab561a1SDavid Mosberger-Tang 					preempt_enable();
1432ab561a1SDavid Mosberger-Tang 				ia64_load_scratch_fpregs(fr);
1442ab561a1SDavid Mosberger-Tang 				return 0;
1452ab561a1SDavid Mosberger-Tang 			}
1462ab561a1SDavid Mosberger-Tang 		}
1472ab561a1SDavid Mosberger-Tang 		p += ESI_DESC_SIZE(*p);
1482ab561a1SDavid Mosberger-Tang 	}
1492ab561a1SDavid Mosberger-Tang 	return -1;
1502ab561a1SDavid Mosberger-Tang }
1512ab561a1SDavid Mosberger-Tang EXPORT_SYMBOL_GPL(ia64_esi_call);
1522ab561a1SDavid Mosberger-Tang 
1532ab561a1SDavid Mosberger-Tang int ia64_esi_call_phys (efi_guid_t guid, struct ia64_sal_retval *isrvp,
1542ab561a1SDavid Mosberger-Tang 			u64 func, u64 arg1, u64 arg2, u64 arg3, u64 arg4,
1552ab561a1SDavid Mosberger-Tang 			u64 arg5, u64 arg6, u64 arg7)
1562ab561a1SDavid Mosberger-Tang {
1572ab561a1SDavid Mosberger-Tang 	struct ia64_fpreg fr[6];
1582ab561a1SDavid Mosberger-Tang 	unsigned long flags;
1592ab561a1SDavid Mosberger-Tang 	u64 esi_params[8];
1602ab561a1SDavid Mosberger-Tang 	char *p;
1612ab561a1SDavid Mosberger-Tang 	int i;
1622ab561a1SDavid Mosberger-Tang 
1632ab561a1SDavid Mosberger-Tang 	if (!esi_systab)
1642ab561a1SDavid Mosberger-Tang 		return -1;
1652ab561a1SDavid Mosberger-Tang 
1662ab561a1SDavid Mosberger-Tang 	p = (char *) (esi_systab + 1);
1672ab561a1SDavid Mosberger-Tang 	for (i = 0; i < esi_systab->entry_count; i++) {
1682ab561a1SDavid Mosberger-Tang 		if (*p == ESI_DESC_ENTRY_POINT) {
1692ab561a1SDavid Mosberger-Tang 			ia64_esi_desc_entry_point_t *esi = (void *)p;
1702ab561a1SDavid Mosberger-Tang 			if (!efi_guidcmp(guid, esi->guid)) {
1712ab561a1SDavid Mosberger-Tang 				ia64_sal_handler esi_proc;
1722ab561a1SDavid Mosberger-Tang 				struct pdesc pdesc;
1732ab561a1SDavid Mosberger-Tang 
1742ab561a1SDavid Mosberger-Tang 				pdesc.addr = (void *)esi->esi_proc;
1752ab561a1SDavid Mosberger-Tang 				pdesc.gp = (void *)esi->gp;
1762ab561a1SDavid Mosberger-Tang 
1772ab561a1SDavid Mosberger-Tang 				esi_proc = (ia64_sal_handler) &pdesc;
1782ab561a1SDavid Mosberger-Tang 
1792ab561a1SDavid Mosberger-Tang 				esi_params[0] = func;
1802ab561a1SDavid Mosberger-Tang 				esi_params[1] = arg1;
1812ab561a1SDavid Mosberger-Tang 				esi_params[2] = arg2;
1822ab561a1SDavid Mosberger-Tang 				esi_params[3] = arg3;
1832ab561a1SDavid Mosberger-Tang 				esi_params[4] = arg4;
1842ab561a1SDavid Mosberger-Tang 				esi_params[5] = arg5;
1852ab561a1SDavid Mosberger-Tang 				esi_params[6] = arg6;
1862ab561a1SDavid Mosberger-Tang 				esi_params[7] = arg7;
1872ab561a1SDavid Mosberger-Tang 				ia64_save_scratch_fpregs(fr);
1882ab561a1SDavid Mosberger-Tang 				spin_lock_irqsave(&sal_lock, flags);
1892ab561a1SDavid Mosberger-Tang 				*isrvp = esi_call_phys(esi_proc, esi_params);
1902ab561a1SDavid Mosberger-Tang 				spin_unlock_irqrestore(&sal_lock, flags);
1912ab561a1SDavid Mosberger-Tang 				ia64_load_scratch_fpregs(fr);
1922ab561a1SDavid Mosberger-Tang 				return 0;
1932ab561a1SDavid Mosberger-Tang 			}
1942ab561a1SDavid Mosberger-Tang 		}
1952ab561a1SDavid Mosberger-Tang 		p += ESI_DESC_SIZE(*p);
1962ab561a1SDavid Mosberger-Tang 	}
1972ab561a1SDavid Mosberger-Tang 	return -1;
1982ab561a1SDavid Mosberger-Tang }
1992ab561a1SDavid Mosberger-Tang EXPORT_SYMBOL_GPL(ia64_esi_call_phys);
2002ab561a1SDavid Mosberger-Tang 
2012ab561a1SDavid Mosberger-Tang static void __exit esi_exit (void)
2022ab561a1SDavid Mosberger-Tang {
2032ab561a1SDavid Mosberger-Tang }
2042ab561a1SDavid Mosberger-Tang 
2052ab561a1SDavid Mosberger-Tang module_init(esi_init);
2062ab561a1SDavid Mosberger-Tang module_exit(esi_exit);	/* makes module removable... */
207