From: Al Stone al.stone@linaro.org
This patch introduces the basic structure of GUFI and sets up an initial API call (gufi_find_first_node(), specifically).
Signed-off-by: Al Stone al.stone@linaro.org --- drivers/gufi/Makefile | 11 ++ drivers/gufi/acpi_protocol.c | 35 +++++ drivers/gufi/acpi_protocol.h | 37 ++++++ drivers/gufi/core.c | 296 +++++++++++++++++++++++++++++++++++++++++++ drivers/gufi/of_protocol.c | 44 +++++++ drivers/gufi/of_protocol.h | 37 ++++++ include/linux/gufi.h | 62 +++++++++ 7 files changed, 522 insertions(+) create mode 100644 drivers/gufi/Makefile create mode 100644 drivers/gufi/acpi_protocol.c create mode 100644 drivers/gufi/acpi_protocol.h create mode 100644 drivers/gufi/core.c create mode 100644 drivers/gufi/of_protocol.c create mode 100644 drivers/gufi/of_protocol.h create mode 100644 include/linux/gufi.h
diff --git a/drivers/gufi/Makefile b/drivers/gufi/Makefile new file mode 100644 index 0000000..eb1b0be --- /dev/null +++ b/drivers/gufi/Makefile @@ -0,0 +1,11 @@ +# +# Makefile for the Grand Unified Firmware Interface +# + +CFLAGS_core.o += -DDEBUG +CFLAGS_of_protocol.o += -DDEBUG +CFLAGS_acpi_protocol.o += -DDEBUG + +obj-$(CONFIG_GUFI) += core.o +obj-$(CONFIG_GUFI) += of_protocol.o +obj-$(CONFIG_GUFI) += acpi_protocol.o diff --git a/drivers/gufi/acpi_protocol.c b/drivers/gufi/acpi_protocol.c new file mode 100644 index 0000000..6b75387 --- /dev/null +++ b/drivers/gufi/acpi_protocol.c @@ -0,0 +1,35 @@ +/* + * Grand Unified Firmware Interface + * + * Copyright (C) 2014 Al Stone al.stone@linaro.org + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#ifdef CONFIG_GUFI + +#include "acpi_protocol.h" + +struct gufi_device_node *gufi_acpi_find_first_node(const char *name) +{ + /* TODO */ + return NULL; +} + +#endif /* CONFIG_GUFI */ diff --git a/drivers/gufi/acpi_protocol.h b/drivers/gufi/acpi_protocol.h new file mode 100644 index 0000000..5688f26 --- /dev/null +++ b/drivers/gufi/acpi_protocol.h @@ -0,0 +1,37 @@ +/* + * Grand Unified Firmware Interface + * + * Copyright (C) 2014 Al Stone al.stone@linaro.org + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#ifndef _GUFI_ACPI_PROTOCOL_H +#define _GUFI_ACPI_PROTOCOL_H + +#ifdef CONFIG_GUFI + +#include <linux/gufi.h> +#include <linux/of.h> + +extern struct gufi_device_node *gufi_acpi_find_first_node(const char *name); + +#endif /* CONFIG_GUFI */ + +#endif /*_GUFI_ACPI_PROTOCOL_H */ diff --git a/drivers/gufi/core.c b/drivers/gufi/core.c new file mode 100644 index 0000000..c46af6b --- /dev/null +++ b/drivers/gufi/core.c @@ -0,0 +1,296 @@ +/* + * Grand Unified Firmware Interface + * + * Copyright (C) 2014 Al Stone al.stone@linaro.org + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#ifdef CONFIG_GUFI + +#include <linux/gufi.h> +#include <linux/init.h> +#include <linux/spinlock.h> + +#include "of_protocol.h" +#include "acpi_protocol.h" + +/* + * TODO list: + * + * (1) how do we sync up with the ACPI interpreter being enabled? + * or do we? i think we will have to so that we can execute methods + * properly. but, does that delay all the other drivers from getting + * initialized? + * + */ + +enum search_preference { + SEARCH_ACPI_ONLY, + SEARCH_DT_ONLY, + SEARCH_ACPI_FIRST, + SEARCH_DT_FIRST +}; + +#if IS_ENABLED(CONFIG_GUFI_ACPI_ONLY) +#define DEFAULT_PREFERENCE SEARCH_ACPI_ONLY +#elif IS_ENABLED(CONFIG_GUFI_DT_ONLY) +#define DEFAULT_PREFERENCE SEARCH_DT_ONLY +#elif IS_ENABLED(CONFIG_GUFI_ACPI_FIRST) +#define DEFAULT_PREFERENCE SEARCH_ACPI_FIRST +#elif IS_ENABLED(CONFIG_GUFI_DT_FIRST) +#define DEFAULT_PREFERENCE SEARCH_DT_FIRST +#else +#define DEFAULT_PREFERENCE SEARCH_ACPI_FIRST +#endif + +static enum search_preference howto_search = DEFAULT_PREFERENCE; + +static LIST_HEAD(__protocols); +static LIST_HEAD(__gdn_list); + +static DEFINE_SPINLOCK(__gdn_list_lock); + +/*********************************************************************** + * + * Functions to get different mechanisms registered for finding + * configuration information for a device. + * + ***********************************************************************/ + +int gufi_register_protocol(struct gufi_protocol *prot) +{ + struct gufi_protocol *p; + struct gufi_protocol *tmp = NULL; + + if (!prot) + return -ENODEV; + pr_debug("%s: registering %s\n", __func__, prot->name); + + list_for_each_entry(p, &__protocols, entry) { + if (!strcmp(prot->name, p->name)) { + tmp = p; + break; + } + } + + if (!tmp) + list_add_tail(&prot->entry, &__protocols); + + return 0; +} +EXPORT_SYMBOL(gufi_register_protocol); + +void gufi_unregister_protocol(struct gufi_protocol *prot) +{ + struct gufi_protocol *p; + struct gufi_protocol *tmp = NULL; + + if (!prot) + return; + pr_debug("%s: unregistering %s\n", __func__, prot->name); + + list_for_each_entry(p, &__protocols, entry) { + if (!strcmp(prot->name, p->name)) { + tmp = p; + break; + } + } + + if (tmp) + list_del(&tmp->entry); + + return; +} +EXPORT_SYMBOL(gufi_unregister_protocol); + +/*********************************************************************** + * + * General utility functions + * + ***********************************************************************/ + +/** + * __gufi_look_for_acpi - All gufi_device_nodes are kept in a list. + * Given an acpi_device, search the list for + * a matching node. + * @an: struct acpi_device to look for + * + * Returns a pointer to the node found, if any. + */ +static struct gufi_device_node *__gufi_look_for_acpi(struct acpi_device *an) +{ + struct gufi_device_node *pos; + + if (!an) + return NULL; + + list_for_each_entry(pos, &__gdn_list, entry) { + if (pos->an == an) { + return pos; + } + } + + return NULL; +} + +/** + * __gufi_look_for_dt - All gufi_device_nodes are kept in a list. + * Given an DT device node, search the list for + * a matching node. + * @dn: struct device_node to look for + * + * Returns a pointer to the node found, if any. + */ +static struct gufi_device_node *__gufi_look_for_dt(struct device_node *dn) +{ + struct gufi_device_node *pos; + + if (!dn) + return NULL; + + list_for_each_entry(pos, &__gdn_list, entry) { + if (pos->dn == dn) { + return pos; + } + } + + return NULL; +} + +/** + * gufi_look_for_node - All gufi_device_nodes a kept in a list. + * Given either a device_node or acpi_device + * (or both), search the list for a matching + * node. If there is no node, make one and + * add it to the list. + * @dn: struct device_node to look for + * @an: struct acpi_device to look for + * + * Returns a pointer to the node found, if any, or creates a new node + * and returns the address to it. + */ +struct gufi_device_node *gufi_look_for_node(struct device_node *dn, + struct acpi_device *an) +{ + struct gufi_device_node *gdn = NULL; + struct gufi_device_node *ga = NULL; + struct gufi_device_node *gd = NULL; + unsigned long lock_flags; + + pr_debug("GUFI: entering gufi_look_for_node\n"); + pr_debug("GUFI: gufi_look_for_node: dn = 0x%p\n", dn); + pr_debug("GUFI: gufi_look_for_node: an = 0x%p\n", an); + + spin_lock_irqsave(&__gdn_list_lock, lock_flags); + + ga = __gufi_look_for_acpi(an); + gd = __gufi_look_for_dt(dn); + pr_debug("GUFI: gufi_look_for_node: ga = 0x%p\n", ga); + pr_debug("GUFI: gufi_look_for_node: gd = 0x%p\n", gd); + + if ((ga || gd ) && (ga == gd)) + return ga ? ga : gd; + + if (!(ga || gd)) { + gdn = kzalloc(sizeof(struct gufi_device_node), GFP_KERNEL); + if (gdn) { + gdn->dn = dn; + gdn->an = an; + pr_debug("GUFI: gufi_look_for_node: gdn = 0x%p\n", gdn); + pr_debug("GUFI: gufi_look_for_node: gdn->an = 0x%p\n", gdn->an); + pr_debug("GUFI: gufi_look_for_node: gdn->dn = 0x%p\n", gdn->dn); + kref_init(&gdn->kref); + list_add_tail(&gdn->entry, &__gdn_list); + } + } + spin_unlock_irqrestore(&__gdn_list_lock, lock_flags); + + pr_debug("GUFI: leaving gufi_look_for_node\n"); + + return gdn; +} +EXPORT_SYMBOL(gufi_look_for_node); + + +/*********************************************************************** + * + * Functions that implement the elements of the GUFI API + * + ***********************************************************************/ + +struct gufi_device_node *gufi_find_first_node(const char *name) +{ + struct gufi_device_node *result = NULL; + struct gufi_protocol *p; + + list_for_each_entry(p, &__protocols, entry) { + if (p->find_first_node) + result = p->find_first_node(name); + if (result) + break; + } + + return result; +} +EXPORT_SYMBOL(gufi_find_first_node); + +static struct gufi_protocol acpi_protocol = { + .name = "ACPI", + .find_first_node = gufi_acpi_find_first_node, +}; + +static struct gufi_protocol of_protocol = { + .name = "OF", + .find_first_node = gufi_of_find_first_node, +}; + +int __init gufi_init(void) +{ + /* + * TODO: enable a kernel parameter that would allow + * this to be switched at boot time. + */ + + switch (howto_search) { + case SEARCH_ACPI_ONLY: + gufi_register_protocol(&acpi_protocol); + break; + ;; + case SEARCH_DT_ONLY: + gufi_register_protocol(&of_protocol); + break; + ;; + case SEARCH_ACPI_FIRST: + gufi_register_protocol(&acpi_protocol); + gufi_register_protocol(&of_protocol); + break; + ;; + case SEARCH_DT_FIRST: + gufi_register_protocol(&of_protocol); + gufi_register_protocol(&acpi_protocol); + break; + ;; + } + + return 0; +} +EXPORT_SYMBOL(gufi_init); + +#endif /* CONFIG_GUFI */ diff --git a/drivers/gufi/of_protocol.c b/drivers/gufi/of_protocol.c new file mode 100644 index 0000000..20af559 --- /dev/null +++ b/drivers/gufi/of_protocol.c @@ -0,0 +1,44 @@ +/* + * Grand Unified Firmware Interface + * + * Copyright (C) 2014 Al Stone al.stone@linaro.org + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#ifdef CONFIG_GUFI + +#include <linux/gufi.h> +#include <linux/of.h> + +#include "of_protocol.h" + +struct gufi_device_node *gufi_of_find_first_node(const char *name) +{ + struct device_node *node; + struct gufi_device_node *gdn; + + node = of_find_compatible_node(NULL, NULL, name); + + gdn = gufi_look_for_node(node, NULL); + + return gdn; +} + +#endif /* CONFIG_GUFI */ diff --git a/drivers/gufi/of_protocol.h b/drivers/gufi/of_protocol.h new file mode 100644 index 0000000..3051bb6 --- /dev/null +++ b/drivers/gufi/of_protocol.h @@ -0,0 +1,37 @@ +/* + * Grand Unified Firmware Interface + * + * Copyright (C) 2014 Al Stone al.stone@linaro.org + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#ifndef _GUFI_OF_PROTOCOL_H +#define _GUFI_OF_PROTOCOL_H + +#ifdef CONFIG_GUFI + +#include <linux/gufi.h> +#include <linux/of.h> + +extern struct gufi_device_node *gufi_of_find_first_node(const char *name); + +#endif /* CONFIG_GUFI */ + +#endif /*_GUFI_OF_PROTOCOL_H */ diff --git a/include/linux/gufi.h b/include/linux/gufi.h new file mode 100644 index 0000000..baf8650 --- /dev/null +++ b/include/linux/gufi.h @@ -0,0 +1,62 @@ +/* + * gufi.h - Grand Unified Firmware Interface + * + * Copyright (C) 2014 Al Stone al.stone@linaro.org + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#ifndef _LINUX_GUFI_H +#define _LINUX_GUFI_H + +#ifdef CONFIG_GUFI + +#include <linux/acpi.h> +#include <linux/errno.h> +#include <linux/kref.h> +#include <linux/list.h> +#include <linux/of.h> +#include <linux/types.h> + +struct gufi_device_node { + struct device_node *dn; + struct acpi_device *an; + struct kref kref; + struct list_head entry; +}; + +struct gufi_protocol { + const char *name; + struct list_head entry; + + struct gufi_device_node *(*find_first_node)(const char *name); +}; + +extern int gufi_init(void); +extern int gufi_register_protocol(struct gufi_protocol *prot); +extern void gufi_unregister_protocol(struct gufi_protocol *prot); + +extern struct gufi_device_node *gufi_look_for_node(struct device_node *dn, + struct acpi_device *an); + +extern struct gufi_device_node *gufi_find_first_node(const char *name); + +#endif /* CONFIG_GUFI */ + +#endif /*_LINUX_GUFI_H */