Commit 4e455c67 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'sfi-release' of git://git.kernel.org/pub/scm/linux/kernel/git/lenb/linux-sfi-2.6

* 'sfi-release' of git://git.kernel.org/pub/scm/linux/kernel/git/lenb/linux-sfi-2.6:
  SFI: add sysfs interface for SFI tables.
  SFI: add support for v0.81 spec
parents 105a048a dce80a56
What: /sys/firmware/sfi/tables/
Date: May 2010
Contact: Len Brown <lenb@kernel.org>
Description:
SFI defines a number of small static memory tables
so the kernel can get platform information from firmware.
The tables are defined in the latest SFI specification:
http://simplefirmware.org/documentation
While the tables are used by the kernel, user-space
can observe them this way:
# cd /sys/firmware/sfi/tables
# cat $TABLENAME > $TABLENAME.bin
...@@ -173,3 +173,44 @@ int sfi_acpi_table_parse(char *signature, char *oem_id, char *oem_table_id, ...@@ -173,3 +173,44 @@ int sfi_acpi_table_parse(char *signature, char *oem_id, char *oem_table_id,
sfi_acpi_put_table(table); sfi_acpi_put_table(table);
return ret; return ret;
} }
static ssize_t sfi_acpi_table_show(struct file *filp, struct kobject *kobj,
struct bin_attribute *bin_attr, char *buf,
loff_t offset, size_t count)
{
struct sfi_table_attr *tbl_attr =
container_of(bin_attr, struct sfi_table_attr, attr);
struct acpi_table_header *th = NULL;
struct sfi_table_key key;
ssize_t cnt;
key.sig = tbl_attr->name;
key.oem_id = NULL;
key.oem_table_id = NULL;
th = sfi_acpi_get_table(&key);
if (!th)
return 0;
cnt = memory_read_from_buffer(buf, count, &offset,
th, th->length);
sfi_acpi_put_table(th);
return cnt;
}
void __init sfi_acpi_sysfs_init(void)
{
u32 tbl_cnt, i;
struct sfi_table_attr *tbl_attr;
tbl_cnt = XSDT_GET_NUM_ENTRIES(xsdt_va, u64);
for (i = 0; i < tbl_cnt; i++) {
tbl_attr =
sfi_sysfs_install_table(xsdt_va->table_offset_entry[i]);
tbl_attr->attr.read = sfi_acpi_table_show;
}
return;
}
...@@ -67,6 +67,7 @@ ...@@ -67,6 +67,7 @@
#include <linux/acpi.h> #include <linux/acpi.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/sfi.h> #include <linux/sfi.h>
#include <linux/slab.h>
#include "sfi_core.h" #include "sfi_core.h"
...@@ -382,6 +383,102 @@ static __init int sfi_find_syst(void) ...@@ -382,6 +383,102 @@ static __init int sfi_find_syst(void)
return -1; return -1;
} }
static struct kobject *sfi_kobj;
static struct kobject *tables_kobj;
static ssize_t sfi_table_show(struct file *filp, struct kobject *kobj,
struct bin_attribute *bin_attr, char *buf,
loff_t offset, size_t count)
{
struct sfi_table_attr *tbl_attr =
container_of(bin_attr, struct sfi_table_attr, attr);
struct sfi_table_header *th = NULL;
struct sfi_table_key key;
ssize_t cnt;
key.sig = tbl_attr->name;
key.oem_id = NULL;
key.oem_table_id = NULL;
if (strncmp(SFI_SIG_SYST, tbl_attr->name, SFI_SIGNATURE_SIZE)) {
th = sfi_get_table(&key);
if (!th)
return 0;
cnt = memory_read_from_buffer(buf, count, &offset,
th, th->len);
sfi_put_table(th);
} else
cnt = memory_read_from_buffer(buf, count, &offset,
syst_va, syst_va->header.len);
return cnt;
}
struct sfi_table_attr __init *sfi_sysfs_install_table(u64 pa)
{
struct sfi_table_attr *tbl_attr;
struct sfi_table_header *th;
int ret;
tbl_attr = kzalloc(sizeof(struct sfi_table_attr), GFP_KERNEL);
if (!tbl_attr)
return NULL;
th = sfi_map_table(pa);
if (!th || !th->sig[0]) {
kfree(tbl_attr);
return NULL;
}
sysfs_attr_init(&tbl_attr->attr.attr);
memcpy(tbl_attr->name, th->sig, SFI_SIGNATURE_SIZE);
tbl_attr->attr.size = 0;
tbl_attr->attr.read = sfi_table_show;
tbl_attr->attr.attr.name = tbl_attr->name;
tbl_attr->attr.attr.mode = 0400;
ret = sysfs_create_bin_file(tables_kobj,
&tbl_attr->attr);
if (ret)
kfree(tbl_attr);
sfi_unmap_table(th);
return tbl_attr;
}
static int __init sfi_sysfs_init(void)
{
int tbl_cnt, i;
if (sfi_disabled)
return 0;
sfi_kobj = kobject_create_and_add("sfi", firmware_kobj);
if (!sfi_kobj)
return 0;
tables_kobj = kobject_create_and_add("tables", sfi_kobj);
if (!tables_kobj) {
kobject_put(sfi_kobj);
return 0;
}
sfi_sysfs_install_table(syst_pa);
tbl_cnt = SFI_GET_NUM_ENTRIES(syst_va, u64);
for (i = 0; i < tbl_cnt; i++)
sfi_sysfs_install_table(syst_va->pentry[i]);
sfi_acpi_sysfs_init();
kobject_uevent(sfi_kobj, KOBJ_ADD);
kobject_uevent(tables_kobj, KOBJ_ADD);
pr_info("SFI sysfs interfaces init success\n");
return 0;
}
void __init sfi_init(void) void __init sfi_init(void)
{ {
if (!acpi_disabled) if (!acpi_disabled)
...@@ -390,7 +487,7 @@ void __init sfi_init(void) ...@@ -390,7 +487,7 @@ void __init sfi_init(void)
if (sfi_disabled) if (sfi_disabled)
return; return;
pr_info("Simple Firmware Interface v0.7 http://simplefirmware.org\n"); pr_info("Simple Firmware Interface v0.81 http://simplefirmware.org\n");
if (sfi_find_syst() || sfi_parse_syst() || sfi_platform_init()) if (sfi_find_syst() || sfi_parse_syst() || sfi_platform_init())
disable_sfi(); disable_sfi();
...@@ -414,3 +511,9 @@ void __init sfi_init_late(void) ...@@ -414,3 +511,9 @@ void __init sfi_init_late(void)
sfi_acpi_init(); sfi_acpi_init();
} }
/*
* The reason we put it here becasue we need wait till the /sys/firmware
* is setup, then our interface can be registered in /sys/firmware/sfi
*/
core_initcall(sfi_sysfs_init);
...@@ -61,6 +61,12 @@ struct sfi_table_key{ ...@@ -61,6 +61,12 @@ struct sfi_table_key{
char *oem_table_id; char *oem_table_id;
}; };
/* sysfs interface */
struct sfi_table_attr {
struct bin_attribute attr;
char name[8];
};
#define SFI_ANY_KEY { .sig = NULL, .oem_id = NULL, .oem_table_id = NULL } #define SFI_ANY_KEY { .sig = NULL, .oem_id = NULL, .oem_table_id = NULL }
extern int __init sfi_acpi_init(void); extern int __init sfi_acpi_init(void);
...@@ -68,3 +74,5 @@ extern struct sfi_table_header *sfi_check_table(u64 paddr, ...@@ -68,3 +74,5 @@ extern struct sfi_table_header *sfi_check_table(u64 paddr,
struct sfi_table_key *key); struct sfi_table_key *key);
struct sfi_table_header *sfi_get_table(struct sfi_table_key *key); struct sfi_table_header *sfi_get_table(struct sfi_table_key *key);
extern void sfi_put_table(struct sfi_table_header *table); extern void sfi_put_table(struct sfi_table_header *table);
extern struct sfi_table_attr __init *sfi_sysfs_install_table(u64 pa);
extern void __init sfi_acpi_sysfs_init(void);
...@@ -73,6 +73,8 @@ ...@@ -73,6 +73,8 @@
#define SFI_SIG_SPIB "SPIB" #define SFI_SIG_SPIB "SPIB"
#define SFI_SIG_I2CB "I2CB" #define SFI_SIG_I2CB "I2CB"
#define SFI_SIG_GPEM "GPEM" #define SFI_SIG_GPEM "GPEM"
#define SFI_SIG_DEVS "DEVS"
#define SFI_SIG_GPIO "GPIO"
#define SFI_SIGNATURE_SIZE 4 #define SFI_SIGNATURE_SIZE 4
#define SFI_OEM_ID_SIZE 6 #define SFI_OEM_ID_SIZE 6
...@@ -145,6 +147,27 @@ struct sfi_rtc_table_entry { ...@@ -145,6 +147,27 @@ struct sfi_rtc_table_entry {
u32 irq; u32 irq;
} __packed; } __packed;
struct sfi_device_table_entry {
u8 type; /* bus type, I2C, SPI or ...*/
#define SFI_DEV_TYPE_SPI 0
#define SFI_DEV_TYPE_I2C 1
#define SFI_DEV_TYPE_UART 2
#define SFI_DEV_TYPE_HSI 3
#define SFI_DEV_TYPE_IPC 4
u8 host_num; /* attached to host 0, 1...*/
u16 addr;
u8 irq;
u32 max_freq;
char name[16];
} __packed;
struct sfi_gpio_table_entry {
char controller_name[16];
u16 pin_no;
char pin_name[16];
} __packed;
struct sfi_spi_table_entry { struct sfi_spi_table_entry {
u16 host_num; /* attached to host 0, 1...*/ u16 host_num; /* attached to host 0, 1...*/
u16 cs; /* chip select */ u16 cs; /* chip select */
...@@ -166,7 +189,6 @@ struct sfi_gpe_table_entry { ...@@ -166,7 +189,6 @@ struct sfi_gpe_table_entry {
u16 phys_id; /* physical GPE id */ u16 phys_id; /* physical GPE id */
} __packed; } __packed;
typedef int (*sfi_table_handler) (struct sfi_table_header *table); typedef int (*sfi_table_handler) (struct sfi_table_header *table);
#ifdef CONFIG_SFI #ifdef CONFIG_SFI
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment