From 31f1e845c0d5d9fd243a6cf8d07fec822c335f88 Mon Sep 17 00:00:00 2001 From: Sanjay R Mehta Date: Tue, 27 Sep 2016 16:13:05 +0530 Subject: [PATCH 1/2] yocto amd staging add support to enable and disable IMC to fetch BIOS code Signed-off-by: Sanjay R Mehta --- drivers/staging/Kconfig | 2 + drivers/staging/Makefile | 1 + drivers/staging/amd_imc/Kconfig | 9 ++ drivers/staging/amd_imc/Makefile | 1 + drivers/staging/amd_imc/amd_imc.c | 286 ++++++++++++++++++++++++++++++++++++++ include/linux/amd_imc.h | 69 +++++++++ 6 files changed, 368 insertions(+) create mode 100644 drivers/staging/amd_imc/Kconfig create mode 100644 drivers/staging/amd_imc/Makefile create mode 100644 drivers/staging/amd_imc/amd_imc.c create mode 100644 include/linux/amd_imc.h diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index 5d3b86a..a6ee082 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -110,4 +110,6 @@ source "drivers/staging/wilc1000/Kconfig" source "drivers/staging/most/Kconfig" +source "drivers/staging/amd_imc/Kconfig" + endif # STAGING diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index 30918ed..4f31852 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -47,3 +47,4 @@ obj-$(CONFIG_FB_TFT) += fbtft/ obj-$(CONFIG_FSL_MC_BUS) += fsl-mc/ obj-$(CONFIG_WILC1000) += wilc1000/ obj-$(CONFIG_MOST) += most/ +obj-$(CONFIG_AMD_IMC) += amd_imc/ diff --git a/drivers/staging/amd_imc/Kconfig b/drivers/staging/amd_imc/Kconfig new file mode 100644 index 0000000..abfb724 --- /dev/null +++ b/drivers/staging/amd_imc/Kconfig @@ -0,0 +1,9 @@ +config AMD_IMC + bool "AMD Intergrated Micro Controller support" + depends on PCI && X86_64 + default y + ---help--- + This driver supports AMD Intergrated Micro Controller. + + To compile this driver as a module, choose M here. The module + will be called amd_imc. diff --git a/drivers/staging/amd_imc/Makefile b/drivers/staging/amd_imc/Makefile new file mode 100644 index 0000000..c4837f8 --- /dev/null +++ b/drivers/staging/amd_imc/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_AMD_IMC) += amd_imc.o diff --git a/drivers/staging/amd_imc/amd_imc.c b/drivers/staging/amd_imc/amd_imc.c new file mode 100644 index 0000000..1551bdb --- /dev/null +++ b/drivers/staging/amd_imc/amd_imc.c @@ -0,0 +1,286 @@ +/***************************************************************************** +* +* Copyright (c) 2014, Advanced Micro Devices, Inc. +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of Advanced Micro Devices, Inc. nor the names of +* its contributors may be used to endorse or promote products derived +* from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL ADVANCED MICRO DEVICES, INC. BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +* +***************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int imc_enabled; +static u16 imc_port_addr; +static u8 msg_reg_base_hi; +static u8 msg_reg_base_lo; +static u16 msg_reg_base; + +static struct pci_dev *amd_imc_pci; +static struct platform_device *amd_imc_platform_device; + +static DEFINE_PCI_DEVICE_TABLE(amd_lpc_pci_tbl) = { + {PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_LPC_BRIDGE, PCI_ANY_ID, + PCI_ANY_ID,}, + {} +}; + +void amd_imc_enter_scratch_ram(void) +{ + u8 byte; + + if (!imc_enabled) + return; + + /* Instruct IMC to enter scratch RAM */ + outb(AMD_MSG_REG0, msg_reg_base + AMD_MSG_INDEX_REG_OFFSET); + outb(0, msg_reg_base + AMD_MSG_DATA_REG_OFFSET); + + outb(AMD_MSG_REG1, msg_reg_base + AMD_MSG_INDEX_REG_OFFSET); + outb(AMD_IMC_ENTER_SCRATCH_RAM, msg_reg_base + AMD_MSG_DATA_REG_OFFSET); + + outb(AMD_MSG_SYS_TO_IMC, msg_reg_base + AMD_MSG_INDEX_REG_OFFSET); + outb(AMD_IMC_ROM_OWNERSHIP_SEM, msg_reg_base + + AMD_MSG_DATA_REG_OFFSET); + + /* As per the spec, the firmware may take up to 50ms */ + msleep(50); + + /* read message registger 0 to confirm function completion */ + outb(AMD_MSG_REG0, msg_reg_base + AMD_MSG_INDEX_REG_OFFSET); + byte = inb(msg_reg_base + AMD_MSG_DATA_REG_OFFSET); + + if (byte == AMD_IMC_FUNC_NOT_SUPP) + pr_info("amd_imc: %s not supported\n", __func__); + else if (byte == AMD_IMC_FUNC_COMPLETED) + pr_info("amd_imc: %s completed\n", __func__); +} +EXPORT_SYMBOL_GPL(amd_imc_enter_scratch_ram); + +void amd_imc_exit_scratch_ram(void) +{ + u8 byte; + + if (!imc_enabled) + return; + + /* Instruct IMC to exit scratch RAM */ + outb(AMD_MSG_REG0, msg_reg_base + AMD_MSG_INDEX_REG_OFFSET); + outb(0, msg_reg_base + AMD_MSG_DATA_REG_OFFSET); + + outb(AMD_MSG_REG1, msg_reg_base + AMD_MSG_INDEX_REG_OFFSET); + outb(AMD_IMC_ENTER_SCRATCH_RAM, msg_reg_base + AMD_MSG_DATA_REG_OFFSET); + + outb(AMD_MSG_SYS_TO_IMC, msg_reg_base + AMD_MSG_INDEX_REG_OFFSET); + outb(AMD_IMC_ROM_OWNERSHIP_SEM, msg_reg_base + + AMD_MSG_DATA_REG_OFFSET); + + /* As per the spec, the firmware may take up to 50ms */ + msleep(50); + + /* read message registger 0 to confirm function completion */ + outb(AMD_MSG_REG0, msg_reg_base + AMD_MSG_INDEX_REG_OFFSET); + byte = inb(msg_reg_base + AMD_MSG_DATA_REG_OFFSET); + + if (byte == AMD_IMC_FUNC_NOT_SUPP) + pr_info("amd_imc: %s not supported\n", __func__); + else if (byte == AMD_IMC_FUNC_COMPLETED) + pr_info("amd_imc: %s completed\n", __func__); +} +EXPORT_SYMBOL_GPL(amd_imc_exit_scratch_ram); + +/* +* The PCI Device ID table below is used to identify the platform +* the driver is supposed to work for. Since this is a platform +* driver, we need a way for us to be able to find the correct +* platform when the driver gets loaded, otherwise we should +* bail out. +*/ +static DEFINE_PCI_DEVICE_TABLE(amd_imc_pci_tbl) = { + { PCI_VENDOR_ID_AMD, 0x790B, PCI_ANY_ID, + PCI_ANY_ID, }, + { 0, }, +}; + +static int amd_imc_init(struct platform_device *pdev) +{ + struct pci_dev *dev = NULL; + static u32 imc_strap_status_phys; + void __iomem *imcstrapstatus; + u32 val; + + /* Match the PCI device */ + for_each_pci_dev(dev) { + if (pci_match_id(amd_imc_pci_tbl, dev) != NULL) { + amd_imc_pci = dev; + break; + } + } + + if (!amd_imc_pci) + return -ENODEV; + + + /* ACPI MMIO Base Address */ + val = AMD_GPIO_ACPIMMIO_BASE; + + /* IMCStrapStatus is located at ACPI MMIO Base Address + 0xE80 */ + if (!request_mem_region_exclusive(val + AMD_IMC_STRAP_STATUS_OFFSET, + AMD_IMC_STRAP_STATUS_SIZE, "IMC Strap Status")) { + pr_err("amd_imc: MMIO address 0x%04x already in use\n", + val + AMD_IMC_STRAP_STATUS_OFFSET); + goto exit; + } + + imc_strap_status_phys = val + AMD_IMC_STRAP_STATUS_OFFSET; + + imcstrapstatus = ioremap(imc_strap_status_phys, + AMD_IMC_STRAP_STATUS_SIZE); + if (!imcstrapstatus) { + pr_err("amd_imc: failed to get IMC Strap Status address\n"); + goto unreg_imc_region; + } + + /* Check if IMC is enabled */ + val = ioread32(imcstrapstatus); + if ((val & AMD_IMC_ENABLED) == AMD_IMC_ENABLED) { + struct pci_dev *pdev = NULL; + + pr_info("amd_imc: IMC is enabled\n"); + imc_enabled = 1; + + /* + * In case IMC is enabled, we need to find the IMC port address + * which will be used to send messages to the IMC. The IMC port + * address is stored in bits 1:15 of PCI device 20, function 3, + * offset 0xA4. PCI device 20, function 3 is actually the LPC + * ISA bridge. + */ + for_each_pci_dev(pdev) { + if (pci_match_id(amd_lpc_pci_tbl, pdev) != NULL) + break; + } + + /* Match found. Get the IMC port address */ + if (pdev) { + pci_read_config_word(pdev, AMD_PCI_IMC_PORT_ADDR_REG, + &imc_port_addr); + + /* The actual IMC port address has bit 0 masked out */ + imc_port_addr &= ~AMD_IMC_PORT_ACTIVE; + } + + /* Put device into configuration state */ + outb(AMD_DEVICE_ENTER_CONFIG_STATE, imc_port_addr + + AMD_IMC_INDEX_REG_OFFSET); + + /* Select logical device number 9 */ + outb(AMD_SET_LOGICAL_DEVICE, imc_port_addr + + AMD_IMC_INDEX_REG_OFFSET); + outb(AMD_SET_DEVICE_9, imc_port_addr + + AMD_IMC_DATA_REG_OFFSET); + + /* read high byte of message register base address */ + outb(AMD_MSG_REG_HIGH, imc_port_addr + + AMD_IMC_INDEX_REG_OFFSET); + msg_reg_base_hi = inb(imc_port_addr + AMD_IMC_DATA_REG_OFFSET); + + /* read low byte of message register base address */ + outb(AMD_MSG_REG_LOW, imc_port_addr + + AMD_IMC_INDEX_REG_OFFSET); + msg_reg_base_lo = inb(imc_port_addr + AMD_IMC_DATA_REG_OFFSET); + + msg_reg_base = msg_reg_base_lo | (msg_reg_base_hi << 8); + + /* Get device out of configuration state */ + outb(AMD_DEVICE_EXIT_CONFIG_STATE, imc_port_addr + + AMD_IMC_INDEX_REG_OFFSET); + } else { + pr_info("amd_imc: IMC is disabled\n"); + imc_enabled = 0; + } + + /* Release the region occupied by IMC Strap Status register */ + iounmap(imcstrapstatus); + release_mem_region(imc_strap_status_phys, AMD_IMC_STRAP_STATUS_SIZE); + + return 0; + +unreg_imc_region: + release_mem_region(imc_strap_status_phys, AMD_IMC_STRAP_STATUS_SIZE); +exit: + return -ENODEV; +} + +static struct platform_driver amd_imc_driver = { + .probe = amd_imc_init, + .driver = { + .owner = THIS_MODULE, + .name = IMC_MODULE_NAME, + }, +}; + +static int __init amd_imc_init_module(void) +{ + int err; + + pr_info("AMD IMC Driver v%s\n", IMC_VERSION); + + err = platform_driver_register(&amd_imc_driver); + if (err) + return err; + + amd_imc_platform_device = platform_device_register_simple( + IMC_MODULE_NAME, -1, NULL, 0); + if (IS_ERR(amd_imc_platform_device)) { + err = PTR_ERR(amd_imc_platform_device); + goto unreg_platform_driver; + } + + return 0; + +unreg_platform_driver: + platform_driver_unregister(&amd_imc_driver); + return err; +} + +static void __exit amd_imc_cleanup_module(void) +{ + platform_device_unregister(amd_imc_platform_device); + platform_driver_unregister(&amd_imc_driver); + pr_info("AMD IMC Module Unloaded\n"); +} + +module_init(amd_imc_init_module); +module_exit(amd_imc_cleanup_module); + +MODULE_AUTHOR("Arindam Nath "); +MODULE_DESCRIPTION("AMD IMC driver"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/include/linux/amd_imc.h b/include/linux/amd_imc.h new file mode 100644 index 0000000..b1c03bf --- /dev/null +++ b/include/linux/amd_imc.h @@ -0,0 +1,69 @@ +#ifndef _AMD_IMC_H_ +#define _AMD_IMC_H_ + +/* Module and version information */ +#define IMC_VERSION "0.1" +#define IMC_MODULE_NAME "AMD IMC" +#define IMC_DRIVER_NAME IMC_MODULE_NAME ", v" IMC_VERSION + +#define DRV_NAME "amd_imc" + +/* IO port address for indirect access using the ACPI PM registers */ +#define AMD_IO_PM_INDEX_REG 0xCD6 +#define AMD_IO_PM_DATA_REG 0xCD7 + +#define AMD_GPIO_ACPIMMIO_BASE 0xFED80000 +#define AMD_PM_ACPI_MMIO_BASE0 0x24 +#define AMD_PM_ACPI_MMIO_BASE1 0x25 +#define AMD_PM_ACPI_MMIO_BASE2 0x26 +#define AMD_PM_ACPI_MMIO_BASE3 0x27 + +#define AMD_ACPI_MMIO_ADDR_MASK ~0x1FFF + +/* Offset of IMC Strap Status register in the ACPI MMIO region */ +#define AMD_IMC_STRAP_STATUS_OFFSET 0xE80 + #define AMD_IMC_ENABLED 0x4 +#define AMD_IMC_STRAP_STATUS_SIZE 4 + +#define PCI_DEVICE_ID_AMD_LPC_BRIDGE 0x790E + #define AMD_PCI_IMC_PORT_ADDR_REG 0xA4 + #define AMD_IMC_PORT_ACTIVE 0x0001 + +/* Device configuration state fields */ +#define AMD_DEVICE_ENTER_CONFIG_STATE 0x5A +#define AMD_DEVICE_EXIT_CONFIG_STATE 0xA5 + +/* Global configuration registers */ +#define AMD_SET_LOGICAL_DEVICE 0x07 + #define AMD_SET_DEVICE_9 0x09 +#define AMD_MSG_REG_HIGH 0x60 +#define AMD_MSG_REG_LOW 0x61 + +/* IMC index and data port offsets for indirect access */ +#define AMD_IMC_INDEX_REG_OFFSET 0x00 +#define AMD_IMC_DATA_REG_OFFSET 0x01 + +/* Message register index and data port offsets for indirect access */ +#define AMD_MSG_INDEX_REG_OFFSET 0x00 +#define AMD_MSG_DATA_REG_OFFSET 0x01 + +/* IMC message registers */ +#define AMD_MSG_SYS_TO_IMC 0x80 + #define AMD_IMC_ROM_OWNERSHIP_SEM 0x96 +#define AMD_MSG_REG0 0x82 + #define AMD_IMC_FUNC_NOT_SUPP 0x00 + #define AMD_IMC_FUNC_COMPLETED 0xFA +#define AMD_MSG_REG1 0x83 + #define AMD_IMC_ENTER_SCRATCH_RAM 0xB4 + #define AMD_IMC_EXIT_SCRATCH_RAM 0xB5 + +/* Extern functions */ +#ifdef CONFIG_AMD_IMC +extern void amd_imc_enter_scratch_ram(void); +extern void amd_imc_exit_scratch_ram(void); +#else +void amd_imc_enter_scratch_ram(void) {} +void amd_imc_exit_scratch_ram(void) {} +#endif + +#endif /* _AMD_IMC_H_ */ -- 2.7.4