diff options
| -rw-r--r-- | Documentation/ABI/testing/sysfs-firmware-sfi | 15 | ||||
| -rw-r--r-- | drivers/sfi/sfi_acpi.c | 41 | ||||
| -rw-r--r-- | drivers/sfi/sfi_core.c | 105 | ||||
| -rw-r--r-- | drivers/sfi/sfi_core.h | 8 | ||||
| -rw-r--r-- | include/linux/sfi.h | 24 |
5 files changed, 191 insertions, 2 deletions
diff --git a/Documentation/ABI/testing/sysfs-firmware-sfi b/Documentation/ABI/testing/sysfs-firmware-sfi new file mode 100644 index 000000000000..4be7d44aeacf --- /dev/null +++ b/Documentation/ABI/testing/sysfs-firmware-sfi | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | What: /sys/firmware/sfi/tables/ | ||
| 2 | Date: May 2010 | ||
| 3 | Contact: Len Brown <lenb@kernel.org> | ||
| 4 | Description: | ||
| 5 | SFI defines a number of small static memory tables | ||
| 6 | so the kernel can get platform information from firmware. | ||
| 7 | |||
| 8 | The tables are defined in the latest SFI specification: | ||
| 9 | http://simplefirmware.org/documentation | ||
| 10 | |||
| 11 | While the tables are used by the kernel, user-space | ||
| 12 | can observe them this way: | ||
| 13 | |||
| 14 | # cd /sys/firmware/sfi/tables | ||
| 15 | # cat $TABLENAME > $TABLENAME.bin | ||
diff --git a/drivers/sfi/sfi_acpi.c b/drivers/sfi/sfi_acpi.c index 34aba30eb84b..f5b4ca581541 100644 --- a/drivers/sfi/sfi_acpi.c +++ b/drivers/sfi/sfi_acpi.c | |||
| @@ -173,3 +173,44 @@ int sfi_acpi_table_parse(char *signature, char *oem_id, char *oem_table_id, | |||
| 173 | sfi_acpi_put_table(table); | 173 | sfi_acpi_put_table(table); |
| 174 | return ret; | 174 | return ret; |
| 175 | } | 175 | } |
| 176 | |||
| 177 | static ssize_t sfi_acpi_table_show(struct file *filp, struct kobject *kobj, | ||
| 178 | struct bin_attribute *bin_attr, char *buf, | ||
| 179 | loff_t offset, size_t count) | ||
| 180 | { | ||
| 181 | struct sfi_table_attr *tbl_attr = | ||
| 182 | container_of(bin_attr, struct sfi_table_attr, attr); | ||
| 183 | struct acpi_table_header *th = NULL; | ||
| 184 | struct sfi_table_key key; | ||
| 185 | ssize_t cnt; | ||
| 186 | |||
| 187 | key.sig = tbl_attr->name; | ||
| 188 | key.oem_id = NULL; | ||
| 189 | key.oem_table_id = NULL; | ||
| 190 | |||
| 191 | th = sfi_acpi_get_table(&key); | ||
| 192 | if (!th) | ||
| 193 | return 0; | ||
| 194 | |||
| 195 | cnt = memory_read_from_buffer(buf, count, &offset, | ||
| 196 | th, th->length); | ||
| 197 | sfi_acpi_put_table(th); | ||
| 198 | |||
| 199 | return cnt; | ||
| 200 | } | ||
| 201 | |||
| 202 | |||
| 203 | void __init sfi_acpi_sysfs_init(void) | ||
| 204 | { | ||
| 205 | u32 tbl_cnt, i; | ||
| 206 | struct sfi_table_attr *tbl_attr; | ||
| 207 | |||
| 208 | tbl_cnt = XSDT_GET_NUM_ENTRIES(xsdt_va, u64); | ||
| 209 | for (i = 0; i < tbl_cnt; i++) { | ||
| 210 | tbl_attr = | ||
| 211 | sfi_sysfs_install_table(xsdt_va->table_offset_entry[i]); | ||
| 212 | tbl_attr->attr.read = sfi_acpi_table_show; | ||
| 213 | } | ||
| 214 | |||
| 215 | return; | ||
| 216 | } | ||
diff --git a/drivers/sfi/sfi_core.c b/drivers/sfi/sfi_core.c index b204a0929139..005195958647 100644 --- a/drivers/sfi/sfi_core.c +++ b/drivers/sfi/sfi_core.c | |||
| @@ -67,6 +67,7 @@ | |||
| 67 | #include <linux/acpi.h> | 67 | #include <linux/acpi.h> |
| 68 | #include <linux/init.h> | 68 | #include <linux/init.h> |
| 69 | #include <linux/sfi.h> | 69 | #include <linux/sfi.h> |
| 70 | #include <linux/slab.h> | ||
| 70 | 71 | ||
| 71 | #include "sfi_core.h" | 72 | #include "sfi_core.h" |
| 72 | 73 | ||
| @@ -382,6 +383,102 @@ static __init int sfi_find_syst(void) | |||
| 382 | return -1; | 383 | return -1; |
| 383 | } | 384 | } |
| 384 | 385 | ||
| 386 | static struct kobject *sfi_kobj; | ||
| 387 | static struct kobject *tables_kobj; | ||
| 388 | |||
| 389 | static ssize_t sfi_table_show(struct file *filp, struct kobject *kobj, | ||
| 390 | struct bin_attribute *bin_attr, char *buf, | ||
| 391 | loff_t offset, size_t count) | ||
| 392 | { | ||
| 393 | struct sfi_table_attr *tbl_attr = | ||
| 394 | container_of(bin_attr, struct sfi_table_attr, attr); | ||
| 395 | struct sfi_table_header *th = NULL; | ||
| 396 | struct sfi_table_key key; | ||
| 397 | ssize_t cnt; | ||
| 398 | |||
| 399 | key.sig = tbl_attr->name; | ||
| 400 | key.oem_id = NULL; | ||
| 401 | key.oem_table_id = NULL; | ||
| 402 | |||
| 403 | if (strncmp(SFI_SIG_SYST, tbl_attr->name, SFI_SIGNATURE_SIZE)) { | ||
| 404 | th = sfi_get_table(&key); | ||
| 405 | if (!th) | ||
| 406 | return 0; | ||
| 407 | |||
| 408 | cnt = memory_read_from_buffer(buf, count, &offset, | ||
| 409 | th, th->len); | ||
| 410 | sfi_put_table(th); | ||
| 411 | } else | ||
| 412 | cnt = memory_read_from_buffer(buf, count, &offset, | ||
| 413 | syst_va, syst_va->header.len); | ||
| 414 | |||
| 415 | return cnt; | ||
| 416 | } | ||
| 417 | |||
| 418 | struct sfi_table_attr __init *sfi_sysfs_install_table(u64 pa) | ||
| 419 | { | ||
| 420 | struct sfi_table_attr *tbl_attr; | ||
| 421 | struct sfi_table_header *th; | ||
| 422 | int ret; | ||
| 423 | |||
| 424 | tbl_attr = kzalloc(sizeof(struct sfi_table_attr), GFP_KERNEL); | ||
| 425 | if (!tbl_attr) | ||
| 426 | return NULL; | ||
| 427 | |||
| 428 | th = sfi_map_table(pa); | ||
| 429 | if (!th || !th->sig[0]) { | ||
| 430 | kfree(tbl_attr); | ||
| 431 | return NULL; | ||
| 432 | } | ||
| 433 | |||
| 434 | sysfs_attr_init(&tbl_attr->attr.attr); | ||
| 435 | memcpy(tbl_attr->name, th->sig, SFI_SIGNATURE_SIZE); | ||
| 436 | |||
| 437 | tbl_attr->attr.size = 0; | ||
| 438 | tbl_attr->attr.read = sfi_table_show; | ||
| 439 | tbl_attr->attr.attr.name = tbl_attr->name; | ||
| 440 | tbl_attr->attr.attr.mode = 0400; | ||
| 441 | |||
| 442 | ret = sysfs_create_bin_file(tables_kobj, | ||
| 443 | &tbl_attr->attr); | ||
| 444 | if (ret) | ||
| 445 | kfree(tbl_attr); | ||
| 446 | |||
| 447 | sfi_unmap_table(th); | ||
| 448 | return tbl_attr; | ||
| 449 | } | ||
| 450 | |||
| 451 | static int __init sfi_sysfs_init(void) | ||
| 452 | { | ||
| 453 | int tbl_cnt, i; | ||
| 454 | |||
| 455 | if (sfi_disabled) | ||
| 456 | return 0; | ||
| 457 | |||
| 458 | sfi_kobj = kobject_create_and_add("sfi", firmware_kobj); | ||
| 459 | if (!sfi_kobj) | ||
| 460 | return 0; | ||
| 461 | |||
| 462 | tables_kobj = kobject_create_and_add("tables", sfi_kobj); | ||
| 463 | if (!tables_kobj) { | ||
| 464 | kobject_put(sfi_kobj); | ||
| 465 | return 0; | ||
| 466 | } | ||
| 467 | |||
| 468 | sfi_sysfs_install_table(syst_pa); | ||
| 469 | |||
| 470 | tbl_cnt = SFI_GET_NUM_ENTRIES(syst_va, u64); | ||
| 471 | |||
| 472 | for (i = 0; i < tbl_cnt; i++) | ||
| 473 | sfi_sysfs_install_table(syst_va->pentry[i]); | ||
| 474 | |||
| 475 | sfi_acpi_sysfs_init(); | ||
| 476 | kobject_uevent(sfi_kobj, KOBJ_ADD); | ||
| 477 | kobject_uevent(tables_kobj, KOBJ_ADD); | ||
| 478 | pr_info("SFI sysfs interfaces init success\n"); | ||
| 479 | return 0; | ||
| 480 | } | ||
| 481 | |||
| 385 | void __init sfi_init(void) | 482 | void __init sfi_init(void) |
| 386 | { | 483 | { |
| 387 | if (!acpi_disabled) | 484 | if (!acpi_disabled) |
| @@ -390,7 +487,7 @@ void __init sfi_init(void) | |||
| 390 | if (sfi_disabled) | 487 | if (sfi_disabled) |
| 391 | return; | 488 | return; |
| 392 | 489 | ||
| 393 | pr_info("Simple Firmware Interface v0.7 http://simplefirmware.org\n"); | 490 | pr_info("Simple Firmware Interface v0.81 http://simplefirmware.org\n"); |
| 394 | 491 | ||
| 395 | if (sfi_find_syst() || sfi_parse_syst() || sfi_platform_init()) | 492 | if (sfi_find_syst() || sfi_parse_syst() || sfi_platform_init()) |
| 396 | disable_sfi(); | 493 | disable_sfi(); |
| @@ -414,3 +511,9 @@ void __init sfi_init_late(void) | |||
| 414 | 511 | ||
| 415 | sfi_acpi_init(); | 512 | sfi_acpi_init(); |
| 416 | } | 513 | } |
| 514 | |||
| 515 | /* | ||
| 516 | * The reason we put it here becasue we need wait till the /sys/firmware | ||
| 517 | * is setup, then our interface can be registered in /sys/firmware/sfi | ||
| 518 | */ | ||
| 519 | core_initcall(sfi_sysfs_init); | ||
diff --git a/drivers/sfi/sfi_core.h b/drivers/sfi/sfi_core.h index da82d39e104d..b7cf220d44ec 100644 --- a/drivers/sfi/sfi_core.h +++ b/drivers/sfi/sfi_core.h | |||
| @@ -61,6 +61,12 @@ struct sfi_table_key{ | |||
| 61 | char *oem_table_id; | 61 | char *oem_table_id; |
| 62 | }; | 62 | }; |
| 63 | 63 | ||
| 64 | /* sysfs interface */ | ||
| 65 | struct sfi_table_attr { | ||
| 66 | struct bin_attribute attr; | ||
| 67 | char name[8]; | ||
| 68 | }; | ||
| 69 | |||
| 64 | #define SFI_ANY_KEY { .sig = NULL, .oem_id = NULL, .oem_table_id = NULL } | 70 | #define SFI_ANY_KEY { .sig = NULL, .oem_id = NULL, .oem_table_id = NULL } |
| 65 | 71 | ||
| 66 | extern int __init sfi_acpi_init(void); | 72 | extern int __init sfi_acpi_init(void); |
| @@ -68,3 +74,5 @@ extern struct sfi_table_header *sfi_check_table(u64 paddr, | |||
| 68 | struct sfi_table_key *key); | 74 | struct sfi_table_key *key); |
| 69 | struct sfi_table_header *sfi_get_table(struct sfi_table_key *key); | 75 | struct sfi_table_header *sfi_get_table(struct sfi_table_key *key); |
| 70 | extern void sfi_put_table(struct sfi_table_header *table); | 76 | extern void sfi_put_table(struct sfi_table_header *table); |
| 77 | extern struct sfi_table_attr __init *sfi_sysfs_install_table(u64 pa); | ||
| 78 | extern void __init sfi_acpi_sysfs_init(void); | ||
diff --git a/include/linux/sfi.h b/include/linux/sfi.h index 9a6f7607174e..0299b4ce63db 100644 --- a/include/linux/sfi.h +++ b/include/linux/sfi.h | |||
| @@ -73,6 +73,8 @@ | |||
| 73 | #define SFI_SIG_SPIB "SPIB" | 73 | #define SFI_SIG_SPIB "SPIB" |
| 74 | #define SFI_SIG_I2CB "I2CB" | 74 | #define SFI_SIG_I2CB "I2CB" |
| 75 | #define SFI_SIG_GPEM "GPEM" | 75 | #define SFI_SIG_GPEM "GPEM" |
| 76 | #define SFI_SIG_DEVS "DEVS" | ||
| 77 | #define SFI_SIG_GPIO "GPIO" | ||
| 76 | 78 | ||
| 77 | #define SFI_SIGNATURE_SIZE 4 | 79 | #define SFI_SIGNATURE_SIZE 4 |
| 78 | #define SFI_OEM_ID_SIZE 6 | 80 | #define SFI_OEM_ID_SIZE 6 |
| @@ -145,6 +147,27 @@ struct sfi_rtc_table_entry { | |||
| 145 | u32 irq; | 147 | u32 irq; |
| 146 | } __packed; | 148 | } __packed; |
| 147 | 149 | ||
| 150 | struct sfi_device_table_entry { | ||
| 151 | u8 type; /* bus type, I2C, SPI or ...*/ | ||
| 152 | #define SFI_DEV_TYPE_SPI 0 | ||
| 153 | #define SFI_DEV_TYPE_I2C 1 | ||
| 154 | #define SFI_DEV_TYPE_UART 2 | ||
| 155 | #define SFI_DEV_TYPE_HSI 3 | ||
| 156 | #define SFI_DEV_TYPE_IPC 4 | ||
| 157 | |||
| 158 | u8 host_num; /* attached to host 0, 1...*/ | ||
| 159 | u16 addr; | ||
| 160 | u8 irq; | ||
| 161 | u32 max_freq; | ||
| 162 | char name[16]; | ||
| 163 | } __packed; | ||
| 164 | |||
| 165 | struct sfi_gpio_table_entry { | ||
| 166 | char controller_name[16]; | ||
| 167 | u16 pin_no; | ||
| 168 | char pin_name[16]; | ||
| 169 | } __packed; | ||
| 170 | |||
| 148 | struct sfi_spi_table_entry { | 171 | struct sfi_spi_table_entry { |
| 149 | u16 host_num; /* attached to host 0, 1...*/ | 172 | u16 host_num; /* attached to host 0, 1...*/ |
| 150 | u16 cs; /* chip select */ | 173 | u16 cs; /* chip select */ |
| @@ -166,7 +189,6 @@ struct sfi_gpe_table_entry { | |||
| 166 | u16 phys_id; /* physical GPE id */ | 189 | u16 phys_id; /* physical GPE id */ |
| 167 | } __packed; | 190 | } __packed; |
| 168 | 191 | ||
| 169 | |||
| 170 | typedef int (*sfi_table_handler) (struct sfi_table_header *table); | 192 | typedef int (*sfi_table_handler) (struct sfi_table_header *table); |
| 171 | 193 | ||
| 172 | #ifdef CONFIG_SFI | 194 | #ifdef CONFIG_SFI |
