/*
 * Bluetooth platform data initialization file
 *
 * (C) Copyright 2017 Intel Corporation
 * Author: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
 *
 * 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; version 2
 * of the License.
 */

#include <linux/gpio/machine.h>
#include <linux/pci.h>
#include <linux/platform_device.h>

#include <asm/cpu_device_id.h>
#include <asm/intel-family.h>
#include <asm/intel-mid.h>

struct bt_sfi_data {
	struct device *dev;
	const char *name;
	int (*setup)(struct bt_sfi_data *ddata);
};

static struct gpiod_lookup_table tng_bt_sfi_gpio_table = {
	.dev_id	= "hci_bcm",
	.table	= {
		GPIO_LOOKUP("0000:00:0c.0", -1, "device-wakeup", GPIO_ACTIVE_HIGH),
		GPIO_LOOKUP("0000:00:0c.0", -1, "shutdown",      GPIO_ACTIVE_HIGH),
		GPIO_LOOKUP("0000:00:0c.0", -1, "host-wakeup",   GPIO_ACTIVE_HIGH),
		{ },
	},
};

#define TNG_BT_SFI_GPIO_DEVICE_WAKEUP	"bt_wakeup"
#define TNG_BT_SFI_GPIO_SHUTDOWN	"BT-reset"
#define TNG_BT_SFI_GPIO_HOST_WAKEUP	"bt_uart_enable"

static int __init tng_bt_sfi_setup(struct bt_sfi_data *ddata)
{
	struct gpiod_lookup_table *table = &tng_bt_sfi_gpio_table;
	struct gpiod_lookup *lookup = table->table;
	struct pci_dev *pdev;

	/* Connected to /dev/ttyS0 */
	pdev = pci_get_domain_bus_and_slot(0, 0, PCI_DEVFN(4, 1));
	if (!pdev)
		return -ENODEV;

	ddata->dev = &pdev->dev;
	ddata->name = table->dev_id;

	lookup[0].chip_hwnum = get_gpio_by_name(TNG_BT_SFI_GPIO_DEVICE_WAKEUP);
	lookup[1].chip_hwnum = get_gpio_by_name(TNG_BT_SFI_GPIO_SHUTDOWN);
	lookup[2].chip_hwnum = get_gpio_by_name(TNG_BT_SFI_GPIO_HOST_WAKEUP);

	gpiod_add_lookup_table(table);
	return 0;
}

static struct bt_sfi_data tng_bt_sfi_data __initdata = {
	.setup	= tng_bt_sfi_setup,
};

#define ICPU(model, ddata)	\
	{ X86_VENDOR_INTEL, 6, model, X86_FEATURE_ANY, (kernel_ulong_t)&ddata }

static const struct x86_cpu_id bt_sfi_cpu_ids[] = {
	ICPU(INTEL_FAM6_ATOM_SILVERMONT_MID, tng_bt_sfi_data),
	{}
};

static int __init bt_sfi_init(void)
{
	struct platform_device_info info;
	struct platform_device *pdev;
	const struct x86_cpu_id *id;
	struct bt_sfi_data *ddata;
	int ret;

	id = x86_match_cpu(bt_sfi_cpu_ids);
	if (!id)
		return -ENODEV;

	ddata = (struct bt_sfi_data *)id->driver_data;
	if (!ddata)
		return -ENODEV;

	ret = ddata->setup(ddata);
	if (ret)
		return ret;

	memset(&info, 0, sizeof(info));
	info.fwnode	= ddata->dev->fwnode;
	info.parent	= ddata->dev;
	info.name	= ddata->name,
	info.id		= PLATFORM_DEVID_NONE,

	pdev = platform_device_register_full(&info);
	if (IS_ERR(pdev))
		return PTR_ERR(pdev);

	dev_info(ddata->dev, "Registered Bluetooth device: %s\n", ddata->name);
	return 0;
}
device_initcall(bt_sfi_init);
