[linux-yocto] [PATCH 05/11] x86: Sideband Interface driver for Apollo Lake

Yong, Jonathan jonathan.yong at intel.com
Mon Jun 27 18:28:20 PDT 2016


The Sideband Interface driver for Apollo Lake shares much similarity
with the existing iosf_mbi driver functionality wise, but was rewritten
for Apollo Lake due to incompatibility with the new registers.

Signed-off-by: Yong, Jonathan <jonathan.yong at intel.com>
---
 arch/x86/Kconfig                      |  18 ++
 arch/x86/platform/bxt/Makefile        |   1 +
 arch/x86/platform/bxt/sbi_apl.c       | 399 ++++++++++++++++++++++++++++++++++
 include/linux/platform_data/sbi_apl.h |  67 ++++++
 4 files changed, 485 insertions(+)
 create mode 100644 arch/x86/platform/bxt/Makefile
 create mode 100644 arch/x86/platform/bxt/sbi_apl.c
 create mode 100644 include/linux/platform_data/sbi_apl.h

diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 226d569..8e3f6e2 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -563,6 +563,24 @@ config IOSF_MBI_DEBUG
 
 	  If you don't require the option or are in doubt, say N.
 
+config X86_INTEL_SBI_APL
+	tristate "Intel SoC Sideband Interface for Apollo Lake"
+	depends on PCI
+	---help---
+	  This option enables sideband interface access for Intel Apollo Lake.
+	  Like IOSF_MBI, the registers are used in lieu of MSR's.
+
+	  You should say Y if you are running a kernel on Apollo Lake.
+
+config X86_INTEL_SBI_APL_DEBUG
+	bool "Enable Intel SoC SBI for Apollo Lake access in debugfs"
+	depends on X86_INTEL_SBI_APL && DEBUG_FS
+	---help---
+	  Select this option to expose the Sideband Interface through
+	  debugfs.
+
+	  You should say N if you do not plan to debug the SoC.
+
 config X86_RDC321X
 	bool "RDC R-321x SoC"
 	depends on X86_32
diff --git a/arch/x86/platform/bxt/Makefile b/arch/x86/platform/bxt/Makefile
new file mode 100644
index 0000000..5990592
--- /dev/null
+++ b/arch/x86/platform/bxt/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_X86_INTEL_SBI_APL) += sbi_apl.o
diff --git a/arch/x86/platform/bxt/sbi_apl.c b/arch/x86/platform/bxt/sbi_apl.c
new file mode 100644
index 0000000..1974f5f
--- /dev/null
+++ b/arch/x86/platform/bxt/sbi_apl.c
@@ -0,0 +1,399 @@
+/*
+ * Sideband Interface driver for Apollo Lake
+ * Copyright (c) 2015, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ *
+ *
+ * The Sideband Interface is an access mechanism to communicate with multiple
+ * devices on-board the SoC fabric over a PCI interface. This driver deals with
+ * the Apollo Lake SoC.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/mutex.h>
+#include <linux/pci.h>
+#include <linux/debugfs.h>
+#include <linux/capability.h>
+#include <linux/platform_device.h>
+
+#include <linux/platform_data/sbi_apl.h>
+
+#define DRV_NAME "sbi_apl"
+#define DRV_VERSION "1.0"
+
+static struct sbi_platform_data *plat_data;
+#define sbi_pdev_bus plat_data->bus
+#define sbi_pdev_slot plat_data->p2sb
+#define sbi_apl_lock plat_data->lock
+
+static u32 sbi_address(const struct sbi_apl_message *args)
+{
+	return (((u32)args->port_address) << 24 | args->register_offset);
+}
+
+static u16 sbi_status(const struct sbi_apl_message *args)
+{
+	return ((u16)args->opcode << 8) |
+	args->posted << 7 |
+	args->status << 1;
+}
+
+static u16 sbi_routing(const struct sbi_apl_message *args)
+{
+	return ((u16)args->byte_enable << 12) |
+		args->base_address_register << 8 |
+		args->function_id;
+}
+
+/* returns 0 on OK wait */
+static int sbi_do_wait(struct pci_bus *sbi_pdev, unsigned int devfn, u16 *word)
+{
+	int ret;
+	unsigned int timeout = 0x0FFFFFFF; /*2^28-1*/
+
+	do {
+		ret = pci_bus_read_config_word(
+			sbi_pdev,
+			devfn,
+			SBI_STAT_OFFSET,
+			word);
+		if (ret)
+			return ret;
+		if (*word == (u16) -1) {
+			pr_err("sbi_do_wait busy wait failed, P2SB read not allowed?");
+			return 1;
+		}
+		timeout--;
+	} while (timeout && (*word & SBI_STAT_BUSY_MASK));
+	/* fail if time-out occurs */
+	return timeout ? 0 : 1;
+}
+
+static int sbi_do_write(struct pci_bus *sbi_pdev, unsigned int devfn,
+	struct sbi_apl_message *args)
+{
+	int ret = 0;
+	u16 word;
+
+	if (!sbi_pdev)
+		return -ENODEV;
+	if (!capable(CAP_SYS_RAWIO))
+		return -EACCES;
+	/* Is it busy? */
+	if (sbi_do_wait(sbi_pdev, devfn, &word)) {
+		pr_err(DRV_NAME " device has busy bit set %hx!\n", word);
+		ret = -EAGAIN;
+		goto err;
+	};
+	ret = pci_bus_write_config_dword(sbi_pdev, devfn,
+		SBI_ADDR_OFFSET, sbi_address(args));
+	if (ret)
+		goto err;
+	ret = pci_bus_write_config_dword(sbi_pdev, devfn,
+		SBI_DATA_OFFSET, args->data);
+	if (ret)
+		goto err;
+	ret = pci_bus_write_config_word(sbi_pdev, devfn,
+		SBI_ROUT_OFFSET, sbi_routing(args));
+	if (ret)
+		goto err;
+	ret = pci_bus_write_config_dword(sbi_pdev, devfn,
+		SBI_EADD_OFFSET, args->extended_register_address);
+	if (ret)
+		goto err;
+	ret = pci_bus_write_config_word(sbi_pdev, devfn,
+		SBI_STAT_OFFSET, sbi_status(args) | SBI_STAT_BUSY_MASK);
+	if (ret)
+		goto err;
+
+	/* Wait for busy loop */
+	if (sbi_do_wait(sbi_pdev, devfn, &word)) {
+		pr_err(DRV_NAME " device is not responding %hx!\n", word);
+		ret = -EBUSY;
+		goto err;
+	};
+	args->opcode = (word & 0xff00) >> 8;
+	args->posted = (word & 0x0080) >> 7;
+	args->status = (word & 0x0006) >> 1;
+
+	ret = pci_bus_read_config_dword(sbi_pdev, devfn,
+		SBI_DATA_OFFSET, &args->data);
+	if (ret)
+		goto err;
+	pr_debug(DRV_NAME" read commit got %04x\n", args->data);
+	return 0;
+err:
+	pr_err(DRV_NAME " PCI config access failed with %d\n", ret);
+	return ret;
+}
+
+static int sbi_validate(const struct sbi_apl_message *args)
+{
+	if (args->posted > 0x01 ||
+	args->status > 0x03 ||
+	args->byte_enable > 0x0f ||
+	args->base_address_register > 0x07)
+		return -EINVAL;
+	return 0;
+}
+
+/* hide/unhide P2SB PCI device
+ * hide = 1 device will be hidden
+ * hide = 0 device will be visible
+ */
+static void sbi_hide(struct pci_bus *bus, unsigned int devfn, int hide)
+{
+	pci_bus_write_config_byte(bus, devfn, 0xe1, hide);
+}
+
+/* we don't hide anything if it is somehow not hidden to begin with */
+static int sbi_ishidden(struct pci_bus *bus, unsigned int devfn)
+{
+	u8 ret;
+
+	pci_bus_read_config_byte(bus, devfn, 0xe1, &ret);
+	return (ret) ? 1 : 0;
+}
+
+int sbi_apl_commit(struct sbi_apl_message *args)
+{
+	int ret;
+	struct pci_bus *sbi_pdev;
+
+	ret = sbi_validate(args);
+	if (ret)
+		return ret;
+
+	sbi_pdev = pci_find_bus(0, sbi_pdev_bus);
+	if (!sbi_pdev)
+		return -ENODEV;
+	mutex_lock(sbi_apl_lock);
+	sbi_hide(sbi_pdev, sbi_pdev_slot, 0);
+	ret = sbi_do_write(sbi_pdev, sbi_pdev_slot, args);
+	sbi_hide(sbi_pdev, sbi_pdev_slot, 1);
+	mutex_unlock(sbi_apl_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL(sbi_apl_commit);
+#ifdef CONFIG_X86_INTEL_SBI_APL_DEBUG
+static struct dentry *sbi_dbg;
+static struct sbi_apl_message debug_args;
+
+static void sbi_dbg_cleanup(void)
+{
+	debugfs_remove_recursive(sbi_dbg);
+	sbi_dbg = NULL;
+}
+
+static int dbg_posted_set(void *data, u64 val)
+{
+	*(u8 *)data = !!val;
+	return 0;
+}
+
+static int dbg_status_set(void *data, u64 val)
+{
+	*(u8 *)data = val & SBI_STAT_STATUS_MASK;
+	return 0;
+}
+
+static int dbg_byte_enable_set(void *data, u64 val)
+{
+	*(u8 *)data = val & SBI_STAT_BYTE_ENABLE_MASK;
+	return 0;
+}
+
+static int dbg_base_address_register_fops_set(void *data, u64 val)
+{
+	*(u8 *)data = val & SBI_STAT_BAR_MASK;
+	return 0;
+}
+
+static int dbg_commit_set(void *data, u64 val)
+{
+	return sbi_apl_commit(&debug_args);
+}
+
+static int dbg_u8_get(void *data, u64 *val)
+{
+	*val = *(u8 *)data;
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(posted_fops, dbg_u8_get, dbg_posted_set, "0x%01llx\n");
+DEFINE_SIMPLE_ATTRIBUTE(status_fops, dbg_u8_get, dbg_status_set, "0x%01llx\n");
+DEFINE_SIMPLE_ATTRIBUTE(byte_enable_fops, dbg_u8_get, dbg_byte_enable_set,
+	"0x%01llx\n");
+DEFINE_SIMPLE_ATTRIBUTE(base_address_register_fops, dbg_u8_get,
+	dbg_base_address_register_fops_set, "0x%01llx\n");
+DEFINE_SIMPLE_ATTRIBUTE(commit_fops, NULL, dbg_commit_set, "0x%01llx\n");
+
+static void sbi_dbg_setup(void)
+{
+	struct dentry *d;
+
+	if (!IS_ERR_OR_NULL(sbi_dbg))
+		return;
+	memset(&debug_args, 0, sizeof(debug_args));
+	sbi_dbg = debugfs_create_dir("sbi_apl", NULL);
+	if (IS_ERR_OR_NULL(sbi_dbg))
+		return;
+
+	/* port_address */
+	d = debugfs_create_x8("port_address", 0660, sbi_dbg,
+		&debug_args.port_address);
+	if (!d)
+		goto cleanup;
+
+	/* register_offset */
+	d = debugfs_create_x16("register_offset", 0660, sbi_dbg,
+		&debug_args.register_offset);
+	if (!d)
+		goto cleanup;
+
+	/* opcode */
+	d = debugfs_create_x8("opcode", 0660, sbi_dbg, &debug_args.opcode);
+	if (!d)
+		goto cleanup;
+
+	/* data */
+	d = debugfs_create_x32("data", 0660, sbi_dbg, &debug_args.data);
+	if (!d)
+		goto cleanup;
+
+	/* posted */
+	d = debugfs_create_file("posted", 0660, sbi_dbg, &debug_args.posted,
+		&posted_fops);
+	if (!d)
+		goto cleanup;
+
+	/* status */
+	d = debugfs_create_file("status", 0660, sbi_dbg, &debug_args.status,
+		&status_fops);
+	if (!d)
+		goto cleanup;
+
+	/* byte_enable */
+	d = debugfs_create_file("byte_enable", 0660, sbi_dbg,
+		&debug_args.byte_enable, &byte_enable_fops);
+	if (!d)
+		goto cleanup;
+
+	/* base_address_register */
+	d = debugfs_create_file("base_address_register",
+		0660, sbi_dbg, &debug_args.base_address_register,
+		&base_address_register_fops);
+	if (!d)
+		goto cleanup;
+
+	/* function_id */
+	d = debugfs_create_x8("function_id", 0660, sbi_dbg,
+		&debug_args.function_id);
+	if (!d)
+		goto cleanup;
+
+	/* extended_register_address */
+	d = debugfs_create_x32("extended_register_address", 0660, sbi_dbg,
+		&debug_args.extended_register_address);
+	if (!d)
+		goto cleanup;
+
+	/* commit - initiate write to registers */
+	d = debugfs_create_file("commit", 0220, sbi_dbg, NULL, &commit_fops);
+	if (!d)
+		goto cleanup;
+
+	return;
+
+cleanup:
+	sbi_dbg_cleanup();
+}
+
+#else
+static void sbi_dbg_setup(void)
+{
+}
+static void sbi_dbg_cleanup(void)
+{
+}
+#endif /* CONFIG_X86_INTEL_SBI_DEBUG */
+
+static int sbi_apl_plat_probe(struct platform_device *dev)
+{
+	struct pci_bus *bus = NULL;
+	struct sbi_platform_data *pdata;
+	u32 id;
+	int ret;
+
+	if (!dev)
+		return -ENODEV;
+
+	pdata = dev_get_platdata(&dev->dev);
+
+	pr_info("%s starting, bus %x, device %01x.%x", pdata->name, pdata->bus,
+		PCI_SLOT(pdata->p2sb), PCI_FUNC(pdata->p2sb));
+	bus = pci_find_bus(0, pdata->bus);
+	if (!bus) {
+		pr_warn("Cannot get P2SB bus!");
+		return -ENODEV;
+	}
+	mutex_lock(pdata->lock);
+	sbi_hide(bus, pdata->p2sb, 0);
+
+	if (sbi_ishidden(bus, pdata->p2sb)) {
+		pci_bus_read_config_dword(bus, pdata->p2sb, 0x00, &id);
+		if (id != (u32)-1) {
+			pr_warn("P2SB (0x%08x) might be there but disabled",
+				id);
+		} else {
+			pr_err("Cannot unhideP2SB, is it really there?");
+			ret = -ENODEV;
+			goto out;
+		}
+	}
+	sbi_hide(bus, pdata->p2sb, 1);
+	mutex_unlock(pdata->lock);
+	plat_data = pdata;
+	sbi_dbg_setup();
+	return 0;
+
+out:
+	mutex_unlock(pdata->lock);
+	return ret;
+}
+
+static struct platform_driver sbi_plat_driver = {
+	.probe		= sbi_apl_plat_probe,
+	.driver = {
+		.name = DRV_NAME,
+	},
+};
+
+static void __exit sbi_apl_exit(void)
+{
+	sbi_dbg_cleanup();
+	platform_driver_unregister(&sbi_plat_driver);
+}
+
+static int __init sbi_apl_init(void)
+{
+	pr_info("Apollo Lake Sideband Interface loading");
+	return platform_driver_register(&sbi_plat_driver);
+}
+
+MODULE_DESCRIPTION("Apollo Lake Sideband Interface");
+MODULE_VERSION(DRV_VERSION);
+module_init(sbi_apl_init);
+module_exit(sbi_apl_exit);
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/include/linux/platform_data/sbi_apl.h b/include/linux/platform_data/sbi_apl.h
new file mode 100644
index 0000000..7cd6889
--- /dev/null
+++ b/include/linux/platform_data/sbi_apl.h
@@ -0,0 +1,67 @@
+#ifndef SBI_APL_H
+#define SBI_APL_H
+
+#define SBI_ADDR_OFFSET 0xD0
+#define SBI_DATA_OFFSET 0xD4
+#define SBI_STAT_OFFSET 0xD8
+#define SBI_ROUT_OFFSET 0xDA
+#define SBI_EADD_OFFSET 0xDC
+
+/* Status register */
+#define SBI_STAT_POSTED_MASK      0x01
+#define SBI_STAT_STATUS_MASK      0x03
+#define SBI_STAT_BYTE_ENABLE_MASK 0x0F
+#define SBI_STAT_BAR_MASK         0x07
+#define SBI_STAT_BUSY_MASK        0x01
+
+#define SBI_APL_SET_POSTED(msg, val) \
+	((msg)->posted = ((val) & SBI_STAT_POSTED_MASK))
+#define SBI_APL_SET_STATUS(msg, val) \
+	((msg)->status = ((val) & SBI_STAT_STATUS_MASK))
+#define SBI_APL_SET_BYTE_ENABLE(msg, val) \
+	((msg)->byte_enable = ((val) & SBI_STAT_BYTE_ENABLE_MASK))
+#define SBI_APL_SET_BAR(msg, val) \
+	((msg)->base_address_register = ((val) & SBI_STAT_BAR_MASK))
+
+#define SBI_APL_SET(msg, port, reg, data, opcode, \
+		posted, status, be, bar, funct, eaddr) \
+	do { \
+		(msg)->port_address = (port); \
+		(msg)->register_offset = (reg); \
+		(msg)->data = (data); \
+		SBI_APL_SET_POSTED(msg, opcode); \
+		SBI_STAT_STATUS_MASK(msg, status); \
+		SBI_APL_SET_BYTE_ENABLE(msg, be); \
+		SBI_APL_SET_BAR(msg, bar); \
+		(msg)->function_id = (funct); \
+		(msg)->extended_register_address = (eaddr); \
+	} while (0)
+
+struct sbi_platform_data {
+	const char *name;
+	unsigned int version;
+	unsigned int bus;
+	unsigned int p2sb;
+	struct mutex *lock;
+};
+
+struct sbi_apl_message {
+	u8  port_address;
+	u16 register_offset;
+	u32 data;
+	u8  opcode;
+	/* 1-bit */
+	u8  posted;
+	/* 2-bit */
+	u8  status;
+	/* 4-bit */
+	u8  byte_enable;
+	/* 3-bit */
+	u8  base_address_register;
+	u8  function_id;
+	u32 extended_register_address;
+};
+
+int sbi_apl_commit(struct sbi_apl_message *args);
+
+#endif
-- 
2.7.3



More information about the linux-yocto mailing list