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