// SPDX-License-Identifier: GPL-2.0
#include "pr.h"

#include <linux/dmi.h>
#include <linux/init.h>
#include <linux/kconfig.h>
#include <linux/module.h>
#include <linux/string.h>
#include <linux/types.h>
#include <linux/wmi.h>

#include "ec.h"
#include "wmi.h"

/* submodules */
#include "pdev.h"
#include "events.h"
#include "hwmon.h"
#include "battery.h"

/* ========================================================================== */

#define SUBMODULE_ENTRY(_name, _req) { .name = #_name, .init = ac71_ ## _name ## _setup, .cleanup = ac71_ ## _name ## _cleanup, .required = _req }

static struct ac71_submodule {
	const char *name;

	bool required    : 1,
	     initialized : 1;

	int (*init)(void);
	void (*cleanup)(void);
} ac71_submodules[] __refdata = {
	SUBMODULE_ENTRY(pdev, true), /* must be first */
	SUBMODULE_ENTRY(wmi_events, false),
	SUBMODULE_ENTRY(hwmon, false),
	SUBMODULE_ENTRY(battery, false),
};

#undef SUBMODULE_ENTRY

static void do_cleanup(void)
{
	int i;

	for (i = ARRAY_SIZE(ac71_submodules) - 1; i >= 0; i--) {
		const struct ac71_submodule *sm = &ac71_submodules[i];

		if (sm->initialized)
			sm->cleanup();
	}
}

static const struct dmi_system_id ac71_dmi_table[] __initconst = {
	{
		.matches = {
			DMI_MATCH(DMI_BOARD_NAME, "LAPAC71H"),
			{ }
		}
	}
};

static int __init ac71_module_init(void)
{
	int err = 0, i;

	if (!wmi_has_guid(AC71_WMI_WMBC_GUID)) {
		pr_err("WMI GUID not found\n");
		err = -ENODEV; goto out;
	}

	if (!dmi_check_system(ac71_dmi_table)) {
		pr_err("no DMI match\n");
		err = -ENODEV; goto out;
	}

	err = ec_read_byte(PROJ_ID_ADDR);
	if (err < 0) {
		pr_err("failed to query project id: %d\n", err);
		goto out;
	}

	pr_info("project id: %d\n", err);

	err = ec_read_byte(PLATFORM_ID_ADDR);
	if (err < 0) {
		pr_err("failed to query platform id: %d\n", err);
		goto out;
	}

	pr_info("platform id: %d\n", err);

	for (i = 0; i < ARRAY_SIZE(ac71_submodules); i++) {
		struct ac71_submodule *sm = &ac71_submodules[i];

		err = sm->init();
		if (err) {
			pr_warn("failed to initialize %s submodule: %d\n", sm->name, err);
			if (sm->required)
				goto out;
		} else {
			sm->initialized = true;
		}
	}

	err = 0;

out:
	if (err)
		do_cleanup();
	else
		pr_info("module loaded\n");

	return err;
}

static void __exit ac71_module_cleanup(void)
{
	do_cleanup();
	pr_info("module unloaded\n");
}

/* ========================================================================== */

module_init(ac71_module_init);
module_exit(ac71_module_cleanup);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("AC71 laptop platform driver");
MODULE_ALIAS("wmi:" AC71_WMI_WMBC_GUID);