Version:  2.0.40 2.2.26 2.4.37 3.0 3.1 3.2 3.3 3.4 3.5 3.6 3.7 3.8 3.9 3.10 3.11 3.12 3.13 3.14 3.15 3.16

Linux/arch/ia64/hp/common/aml_nfw.c

  1 /*
  2  * OpRegion handler to allow AML to call native firmware
  3  *
  4  * (c) Copyright 2007 Hewlett-Packard Development Company, L.P.
  5  *      Bjorn Helgaas <bjorn.helgaas@hp.com>
  6  *
  7  * This program is free software; you can redistribute it and/or modify
  8  * it under the terms of the GNU General Public License version 2 as
  9  * published by the Free Software Foundation.
 10  *
 11  * This driver implements HP Open Source Review Board proposal 1842,
 12  * which was approved on 9/20/2006.
 13  *
 14  * For technical documentation, see the HP SPPA Firmware EAS, Appendix F.
 15  *
 16  * ACPI does not define a mechanism for AML methods to call native firmware
 17  * interfaces such as PAL or SAL.  This OpRegion handler adds such a mechanism.
 18  * After the handler is installed, an AML method can call native firmware by
 19  * storing the arguments and firmware entry point to specific offsets in the
 20  * OpRegion.  When AML reads the "return value" offset from the OpRegion, this
 21  * handler loads up the arguments, makes the firmware call, and returns the
 22  * result.
 23  */
 24 
 25 #include <linux/module.h>
 26 #include <linux/acpi.h>
 27 #include <asm/sal.h>
 28 
 29 MODULE_AUTHOR("Bjorn Helgaas <bjorn.helgaas@hp.com>");
 30 MODULE_LICENSE("GPL");
 31 MODULE_DESCRIPTION("ACPI opregion handler for native firmware calls");
 32 
 33 static bool force_register;
 34 module_param_named(force, force_register, bool, 0);
 35 MODULE_PARM_DESC(force, "Install opregion handler even without HPQ5001 device");
 36 
 37 #define AML_NFW_SPACE           0xA1
 38 
 39 struct ia64_pdesc {
 40         void *ip;
 41         void *gp;
 42 };
 43 
 44 /*
 45  * N.B.  The layout of this structure is defined in the HP SPPA FW EAS, and
 46  *       the member offsets are embedded in AML methods.
 47  */
 48 struct ia64_nfw_context {
 49         u64 arg[8];
 50         struct ia64_sal_retval ret;
 51         u64 ip;
 52         u64 gp;
 53         u64 pad[2];
 54 };
 55 
 56 static void *virt_map(u64 address)
 57 {
 58         if (address & (1UL << 63))
 59                 return (void *) (__IA64_UNCACHED_OFFSET | address);
 60 
 61         return __va(address);
 62 }
 63 
 64 static void aml_nfw_execute(struct ia64_nfw_context *c)
 65 {
 66         struct ia64_pdesc virt_entry;
 67         ia64_sal_handler entry;
 68 
 69         virt_entry.ip = virt_map(c->ip);
 70         virt_entry.gp = virt_map(c->gp);
 71 
 72         entry = (ia64_sal_handler) &virt_entry;
 73 
 74         IA64_FW_CALL(entry, c->ret,
 75                      c->arg[0], c->arg[1], c->arg[2], c->arg[3],
 76                      c->arg[4], c->arg[5], c->arg[6], c->arg[7]);
 77 }
 78 
 79 static void aml_nfw_read_arg(u8 *offset, u32 bit_width, u64 *value)
 80 {
 81         switch (bit_width) {
 82         case 8:
 83                 *value = *(u8 *)offset;
 84                 break;
 85         case 16:
 86                 *value = *(u16 *)offset;
 87                 break;
 88         case 32:
 89                 *value = *(u32 *)offset;
 90                 break;
 91         case 64:
 92                 *value = *(u64 *)offset;
 93                 break;
 94         }
 95 }
 96 
 97 static void aml_nfw_write_arg(u8 *offset, u32 bit_width, u64 *value)
 98 {
 99         switch (bit_width) {
100         case 8:
101                 *(u8 *) offset = *value;
102                 break;
103         case 16:
104                 *(u16 *) offset = *value;
105                 break;
106         case 32:
107                 *(u32 *) offset = *value;
108                 break;
109         case 64:
110                 *(u64 *) offset = *value;
111                 break;
112         }
113 }
114 
115 static acpi_status aml_nfw_handler(u32 function, acpi_physical_address address,
116         u32 bit_width, u64 *value, void *handler_context,
117         void *region_context)
118 {
119         struct ia64_nfw_context *context = handler_context;
120         u8 *offset = (u8 *) context + address;
121 
122         if (bit_width !=  8 && bit_width != 16 &&
123             bit_width != 32 && bit_width != 64)
124                 return AE_BAD_PARAMETER;
125 
126         if (address + (bit_width >> 3) > sizeof(struct ia64_nfw_context))
127                 return AE_BAD_PARAMETER;
128 
129         switch (function) {
130         case ACPI_READ:
131                 if (address == offsetof(struct ia64_nfw_context, ret))
132                         aml_nfw_execute(context);
133                 aml_nfw_read_arg(offset, bit_width, value);
134                 break;
135         case ACPI_WRITE:
136                 aml_nfw_write_arg(offset, bit_width, value);
137                 break;
138         }
139 
140         return AE_OK;
141 }
142 
143 static struct ia64_nfw_context global_context;
144 static int global_handler_registered;
145 
146 static int aml_nfw_add_global_handler(void)
147 {
148         acpi_status status;
149 
150         if (global_handler_registered)
151                 return 0;
152 
153         status = acpi_install_address_space_handler(ACPI_ROOT_OBJECT,
154                 AML_NFW_SPACE, aml_nfw_handler, NULL, &global_context);
155         if (ACPI_FAILURE(status))
156                 return -ENODEV;
157 
158         global_handler_registered = 1;
159         printk(KERN_INFO "Global 0x%02X opregion handler registered\n",
160                 AML_NFW_SPACE);
161         return 0;
162 }
163 
164 static int aml_nfw_remove_global_handler(void)
165 {
166         acpi_status status;
167 
168         if (!global_handler_registered)
169                 return 0;
170 
171         status = acpi_remove_address_space_handler(ACPI_ROOT_OBJECT,
172                 AML_NFW_SPACE, aml_nfw_handler);
173         if (ACPI_FAILURE(status))
174                 return -ENODEV;
175 
176         global_handler_registered = 0;
177         printk(KERN_INFO "Global 0x%02X opregion handler removed\n",
178                 AML_NFW_SPACE);
179         return 0;
180 }
181 
182 static int aml_nfw_add(struct acpi_device *device)
183 {
184         /*
185          * We would normally allocate a new context structure and install
186          * the address space handler for the specific device we found.
187          * But the HP-UX implementation shares a single global context
188          * and always puts the handler at the root, so we'll do the same.
189          */
190         return aml_nfw_add_global_handler();
191 }
192 
193 static int aml_nfw_remove(struct acpi_device *device)
194 {
195         return aml_nfw_remove_global_handler();
196 }
197 
198 static const struct acpi_device_id aml_nfw_ids[] = {
199         {"HPQ5001", 0},
200         {"", 0}
201 };
202 
203 static struct acpi_driver acpi_aml_nfw_driver = {
204         .name = "native firmware",
205         .ids = aml_nfw_ids,
206         .ops = {
207                 .add = aml_nfw_add,
208                 .remove = aml_nfw_remove,
209                 },
210 };
211 
212 static int __init aml_nfw_init(void)
213 {
214         int result;
215 
216         if (force_register)
217                 aml_nfw_add_global_handler();
218 
219         result = acpi_bus_register_driver(&acpi_aml_nfw_driver);
220         if (result < 0) {
221                 aml_nfw_remove_global_handler();
222                 return result;
223         }
224 
225         return 0;
226 }
227 
228 static void __exit aml_nfw_exit(void)
229 {
230         acpi_bus_unregister_driver(&acpi_aml_nfw_driver);
231         aml_nfw_remove_global_handler();
232 }
233 
234 module_init(aml_nfw_init);
235 module_exit(aml_nfw_exit);
236 

This page was automatically generated by LXR 0.3.1 (source).  •  Linux is a registered trademark of Linus Torvalds  •  Contact us