diff options
author | Aamir Bohra <aamir.bohra@intel.com> | 2017-04-19 18:19:14 +0530 |
---|---|---|
committer | Martin Roth <martinroth@google.com> | 2017-05-08 17:49:38 +0200 |
commit | 52f29743b153e89ca38db5d7a207c676c4c70207 (patch) | |
tree | afb48119cd2a2d8173a9104adc5b131ca41d6f71 | |
parent | 1f355178d6b05c9ba14c010b4304835801345f6a (diff) |
soc/intel/common/block: Add Intel common SMBus code
Add below code support under intel/common/block:
* SMBus read/write byte APIs
* Common SMBus initialization code
Change-Id: I936143a334c31937d557c6828e5876d35b133567
Signed-off-by: Aamir Bohra <aamir.bohra@intel.com>
Reviewed-on: https://review.coreboot.org/19372
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Aaron Durbin <adurbin@chromium.org>
Reviewed-by: Arthur Heymans <arthur@aheymans.xyz>
-rw-r--r-- | src/soc/intel/common/block/include/intelblocks/smbus.h | 22 | ||||
-rw-r--r-- | src/soc/intel/common/block/smbus/Kconfig | 4 | ||||
-rw-r--r-- | src/soc/intel/common/block/smbus/Makefile.inc | 9 | ||||
-rw-r--r-- | src/soc/intel/common/block/smbus/smbus.c | 101 | ||||
-rw-r--r-- | src/soc/intel/common/block/smbus/smbus_early.c | 52 | ||||
-rw-r--r-- | src/soc/intel/common/block/smbus/smbuslib.c | 137 | ||||
-rw-r--r-- | src/soc/intel/common/block/smbus/smbuslib.h | 38 |
7 files changed, 363 insertions, 0 deletions
diff --git a/src/soc/intel/common/block/include/intelblocks/smbus.h b/src/soc/intel/common/block/include/intelblocks/smbus.h new file mode 100644 index 0000000000..262a9e8e65 --- /dev/null +++ b/src/soc/intel/common/block/include/intelblocks/smbus.h @@ -0,0 +1,22 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2017 Intel Corporation. + * + * 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. + * + * This program is distributed in the hope that 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. + */ + +#ifndef SOC_INTEL_COMMON_BLOCK_SMBUS_H +#define SOC_INTEL_COMMON_BLOCK_SMBUS_H + +/* Program SMBus IO base, enable host Controller interface, clear status reg */ +void smbus_common_init(void); + +#endif /* SOC_INTEL_COMMON_BLOCK_SMBUS_H */ diff --git a/src/soc/intel/common/block/smbus/Kconfig b/src/soc/intel/common/block/smbus/Kconfig new file mode 100644 index 0000000000..4514383364 --- /dev/null +++ b/src/soc/intel/common/block/smbus/Kconfig @@ -0,0 +1,4 @@ +config SOC_INTEL_COMMON_BLOCK_SMBUS + bool + help + Intel Processor common SMBus support diff --git a/src/soc/intel/common/block/smbus/Makefile.inc b/src/soc/intel/common/block/smbus/Makefile.inc new file mode 100644 index 0000000000..e33f37f9af --- /dev/null +++ b/src/soc/intel/common/block/smbus/Makefile.inc @@ -0,0 +1,9 @@ +bootblock-$(CONFIG_SOC_INTEL_COMMON_BLOCK_SMBUS) += smbuslib.c +bootblock-$(CONFIG_SOC_INTEL_COMMON_BLOCK_SMBUS) += smbus_early.c + +romstage-$(CONFIG_SOC_INTEL_COMMON_BLOCK_SMBUS) += smbuslib.c +romstage-$(CONFIG_SOC_INTEL_COMMON_BLOCK_SMBUS) += smbus_early.c + +ramstage-$(CONFIG_SOC_INTEL_COMMON_BLOCK_SMBUS) += smbuslib.c +ramstage-$(CONFIG_SOC_INTEL_COMMON_BLOCK_SMBUS) += smbus.c + diff --git a/src/soc/intel/common/block/smbus/smbus.c b/src/soc/intel/common/block/smbus/smbus.c new file mode 100644 index 0000000000..cd066ace58 --- /dev/null +++ b/src/soc/intel/common/block/smbus/smbus.c @@ -0,0 +1,101 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2017 Intel Corporation. + * + * 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. + * + * This program is distributed in the hope that 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. + */ + +#include <device/device.h> +#include <device/path.h> +#include <device/smbus.h> +#include <device/pci.h> +#include <device/pci_ids.h> +#include <device/pci_ops.h> +#include <soc/smbus.h> +#include "smbuslib.h" + +static int lsmbus_read_byte(device_t dev, u8 address) +{ + u16 device; + struct resource *res; + struct bus *pbus; + device = dev->path.i2c.device; + pbus = get_pbus_smbus(dev); + res = find_resource(pbus->dev, PCI_BASE_ADDRESS_4); + return smbus_read8(res->base, device, address); +} + +static int lsmbus_write_byte(device_t dev, u8 address, u8 data) +{ + u16 device; + struct resource *res; + struct bus *pbus; + + device = dev->path.i2c.device; + pbus = get_pbus_smbus(dev); + res = find_resource(pbus->dev, PCI_BASE_ADDRESS_4); + return smbus_write8(res->base, device, address, data); +} + +static struct smbus_bus_operations lops_smbus_bus = { + .read_byte = lsmbus_read_byte, + .write_byte = lsmbus_write_byte, +}; + +static void pch_smbus_init(device_t dev) +{ + struct resource *res; + u16 reg16; + + /* Enable clock gating */ + reg16 = pci_read_config32(dev, 0x80); + reg16 &= ~((1 << 8)|(1 << 10)|(1 << 12)|(1 << 14)); + pci_write_config32(dev, 0x80, reg16); + + /* Set Receive Slave Address */ + res = find_resource(dev, PCI_BASE_ADDRESS_4); + if (res) + outb(SMBUS_SLAVE_ADDR, res->base + SMB_RCV_SLVA); +} + +static void smbus_read_resources(device_t dev) +{ + struct resource *res = new_resource(dev, PCI_BASE_ADDRESS_4); + res->base = SMBUS_IO_BASE; + res->size = 32; + res->limit = res->base + res->size - 1; + res->flags = IORESOURCE_IO | IORESOURCE_FIXED | IORESOURCE_RESERVE | + IORESOURCE_STORED | IORESOURCE_ASSIGNED; + + /* Also add MMIO resource */ + res = pci_get_resource(dev, PCI_BASE_ADDRESS_0); +} + +static struct device_operations smbus_ops = { + .read_resources = &smbus_read_resources, + .set_resources = &pci_dev_set_resources, + .enable_resources = &pci_dev_enable_resources, + .scan_bus = &scan_smbus, + .init = &pch_smbus_init, + .ops_smbus_bus = &lops_smbus_bus, +}; + +static const unsigned short pci_device_ids[] = { + PCI_DEVICE_ID_INTEL_SPT_LP_SMBUS, + PCI_DEVICE_ID_INTEL_SPT_H_SMBUS, + 0 +}; + +static const struct pci_driver pch_smbus __pci_driver = { + .ops = &smbus_ops, + .vendor = PCI_VENDOR_ID_INTEL, + .devices = pci_device_ids, +}; diff --git a/src/soc/intel/common/block/smbus/smbus_early.c b/src/soc/intel/common/block/smbus/smbus_early.c new file mode 100644 index 0000000000..e0c4d9c649 --- /dev/null +++ b/src/soc/intel/common/block/smbus/smbus_early.c @@ -0,0 +1,52 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2017 Intel Corporation. + * + * 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. + * + * This program is distributed in the hope that 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. + */ + +#include <arch/io.h> +#include <device/pci_def.h> +#include <device/early_smbus.h> +#include <intelblocks/smbus.h> +#include <reg_script.h> +#include <soc/pci_devs.h> +#include "smbuslib.h" + +static const struct reg_script smbus_init_script[] = { + /* Set SMBus I/O base address */ + REG_PCI_WRITE32(PCI_BASE_ADDRESS_4, SMBUS_IO_BASE), + /* Set SMBus enable */ + REG_PCI_WRITE8(HOSTC, HST_EN), + /* Enable I/O access */ + REG_PCI_WRITE16(PCI_COMMAND, PCI_COMMAND_IO), + /* Disable interrupts */ + REG_IO_WRITE8(SMBUS_IO_BASE + SMBHSTCTL, 0), + /* Clear errors */ + REG_IO_WRITE8(SMBUS_IO_BASE + SMBHSTSTAT, 0xff), + /* Indicate the end of this array by REG_SCRIPT_END */ + REG_SCRIPT_END, +}; + +u8 smbus_read_byte(u32 smbus_dev, u8 addr, u8 offset) +{ + return smbus_read8(SMBUS_IO_BASE, addr, offset); +} + +u8 smbus_write_byte(u32 smbus_dev, u8 addr, u8 offset, u8 value) +{ + return smbus_write8(SMBUS_IO_BASE, addr, offset, value); +} + +void smbus_common_init(void) +{ + reg_script_run_on_dev(PCH_DEV_SMBUS, smbus_init_script); +} diff --git a/src/soc/intel/common/block/smbus/smbuslib.c b/src/soc/intel/common/block/smbus/smbuslib.c new file mode 100644 index 0000000000..a865abfc30 --- /dev/null +++ b/src/soc/intel/common/block/smbus/smbuslib.c @@ -0,0 +1,137 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2017 Intel Corporation. + * + * 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. + * + * This program is distributed in the hope that 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. + */ + +#include <arch/io.h> +#include <device/smbus_def.h> +#include <timer.h> +#include "smbuslib.h" + +static int smbus_wait_till_ready(u16 smbus_base) +{ + struct stopwatch sw; + unsigned char byte; + + stopwatch_init_msecs_expire(&sw, SMBUS_TIMEOUT); + do { + byte = inb(smbus_base + SMBHSTSTAT); + if (!(byte & 1)) + return 0; + } while (!stopwatch_expired(&sw)); + return -1; +} + +static int smbus_wait_till_done(u16 smbus_base) +{ + struct stopwatch sw; + unsigned char byte; + + stopwatch_init_msecs_expire(&sw, SMBUS_TIMEOUT); + do { + byte = inb(smbus_base + SMBHSTSTAT); + if (!((byte & 1) || (byte & ~((1 << 6) | (1 << 0))) == 0)) + return 0; + } while (!stopwatch_expired(&sw)); + return -1; +} + +int smbus_read8(unsigned int smbus_base, unsigned int device, + unsigned int address) +{ + unsigned char global_status_register; + unsigned char byte; + + if (smbus_wait_till_ready(smbus_base) < 0) + return SMBUS_WAIT_UNTIL_READY_TIMEOUT; + + /* Setup transaction */ + /* Disable interrupts */ + outb(inb(smbus_base + SMBHSTCTL) & (~1), smbus_base + SMBHSTCTL); + /* Set the device I'm talking too */ + outb(((device & 0x7f) << 1) | 1, smbus_base + SMBXMITADD); + /* Set the command/address... */ + outb(address & 0xff, smbus_base + SMBHSTCMD); + /* Set up for a byte data read */ + outb((inb(smbus_base + SMBHSTCTL) & 0xe3) | (0x2 << 2), + (smbus_base + SMBHSTCTL)); + /* Clear any lingering errors, so the transaction will run */ + outb(inb(smbus_base + SMBHSTSTAT), smbus_base + SMBHSTSTAT); + + /* Clear the data byte... */ + outb(0, smbus_base + SMBHSTDAT0); + + /* Start the command */ + outb((inb(smbus_base + SMBHSTCTL) | 0x40), + smbus_base + SMBHSTCTL); + + /* Poll for transaction completion */ + if (smbus_wait_till_done(smbus_base) < 0) + return SMBUS_WAIT_UNTIL_DONE_TIMEOUT; + + global_status_register = inb(smbus_base + SMBHSTSTAT); + + /* Ignore the "In Use" status... */ + global_status_register &= ~(3 << 5); + + /* Read results of transaction */ + byte = inb(smbus_base + SMBHSTDAT0); + if (global_status_register != (1 << 1)) + return SMBUS_ERROR; + + return byte; +} + +int smbus_write8(unsigned int smbus_base, unsigned int device, + unsigned int address, unsigned int data) +{ + unsigned char global_status_register; + + if (smbus_wait_till_ready(smbus_base) < 0) + return SMBUS_WAIT_UNTIL_READY_TIMEOUT; + + /* Setup transaction */ + /* Disable interrupts */ + outb(inb(smbus_base + SMBHSTCTL) & (~1), smbus_base + SMBHSTCTL); + /* Set the device I'm talking too */ + outb(((device & 0x7f) << 1) & ~0x01, smbus_base + SMBXMITADD); + /* Set the command/address... */ + outb(address & 0xff, smbus_base + SMBHSTCMD); + /* Set up for a byte data read */ + outb((inb(smbus_base + SMBHSTCTL) & 0xe3) | (0x2 << 2), + (smbus_base + SMBHSTCTL)); + /* Clear any lingering errors, so the transaction will run */ + outb(inb(smbus_base + SMBHSTSTAT), smbus_base + SMBHSTSTAT); + + /* Clear the data byte... */ + outb(data, smbus_base + SMBHSTDAT0); + + /* Start the command */ + outb((inb(smbus_base + SMBHSTCTL) | 0x40), + smbus_base + SMBHSTCTL); + + /* Poll for transaction completion */ + if (smbus_wait_till_done(smbus_base) < 0) + return SMBUS_WAIT_UNTIL_DONE_TIMEOUT; + + global_status_register = inb(smbus_base + SMBHSTSTAT); + + /* Ignore the "In Use" status... */ + global_status_register &= ~(3 << 5); + + /* Read results of transaction */ + if (global_status_register != (1 << 1)) + return SMBUS_ERROR; + + return 0; +} diff --git a/src/soc/intel/common/block/smbus/smbuslib.h b/src/soc/intel/common/block/smbus/smbuslib.h new file mode 100644 index 0000000000..b5be6ca84e --- /dev/null +++ b/src/soc/intel/common/block/smbus/smbuslib.h @@ -0,0 +1,38 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2017 Intel Corporation. + * + * 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. + * + * This program is distributed in the hope that 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. + */ + +#ifndef SOC_INTEL_COMMON_BLOCK_SMBUS__LIB_H +#define SOC_INTEL_COMMON_BLOCK_SMBUS__LIB_H + +/* SMBus IO Base Address */ +#define SMBUS_IO_BASE 0xefa0 +/* PCI Configuration Space : SMBus */ +#define HOSTC 0x40 +#define HST_EN (1 << 0) +/* SMBus I/O bits. */ +#define SMBHSTSTAT 0x0 +#define SMBHSTCTL 0x2 +#define SMBHSTCMD 0x3 +#define SMBXMITADD 0x4 +#define SMBHSTDAT0 0x5 + +#define SMBUS_TIMEOUT 15 /* 15ms */ + +int smbus_read8(unsigned int smbus_base, unsigned int device, + unsigned int address); +int smbus_write8(unsigned int smbus_base, unsigned int device, + unsigned int address, unsigned int data); + +#endif /* SOC_INTEL_COMMON_BLOCK_SMBUS__LIB_H */ |