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.y...@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 -- _______________________________________________ linux-yocto mailing list linux-yocto@yoctoproject.org https://lists.yoctoproject.org/listinfo/linux-yocto