summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/ABI/testing/sysfs-firmware-sfi15
-rw-r--r--drivers/sfi/sfi_acpi.c41
-rw-r--r--drivers/sfi/sfi_core.c105
-rw-r--r--drivers/sfi/sfi_core.h8
-rw-r--r--include/linux/sfi.h24
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 @@
+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
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,
sfi_acpi_put_table(table);
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;
+}
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 @@
#include <linux/acpi.h>
#include <linux/init.h>
#include <linux/sfi.h>
+#include <linux/slab.h>
#include "sfi_core.h"
@@ -382,6 +383,102 @@ static __init int sfi_find_syst(void)
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)
{
if (!acpi_disabled)
@@ -390,7 +487,7 @@ void __init sfi_init(void)
if (sfi_disabled)
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())
disable_sfi();
@@ -414,3 +511,9 @@ void __init sfi_init_late(void)
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);
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{
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 }
extern int __init sfi_acpi_init(void);
@@ -68,3 +74,5 @@ extern struct sfi_table_header *sfi_check_table(u64 paddr,
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 struct sfi_table_attr __init *sfi_sysfs_install_table(u64 pa);
+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 @@
#define SFI_SIG_SPIB "SPIB"
#define SFI_SIG_I2CB "I2CB"
#define SFI_SIG_GPEM "GPEM"
+#define SFI_SIG_DEVS "DEVS"
+#define SFI_SIG_GPIO "GPIO"
#define SFI_SIGNATURE_SIZE 4
#define SFI_OEM_ID_SIZE 6
@@ -145,6 +147,27 @@ struct sfi_rtc_table_entry {
u32 irq;
} __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 {
u16 host_num; /* attached to host 0, 1...*/
u16 cs; /* chip select */
@@ -166,7 +189,6 @@ struct sfi_gpe_table_entry {
u16 phys_id; /* physical GPE id */
} __packed;
-
typedef int (*sfi_table_handler) (struct sfi_table_header *table);
#ifdef CONFIG_SFI