summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAamir Bohra <aamir.bohra@intel.com>2017-04-19 18:19:14 +0530
committerMartin Roth <martinroth@google.com>2017-05-08 17:49:38 +0200
commit52f29743b153e89ca38db5d7a207c676c4c70207 (patch)
treeafb48119cd2a2d8173a9104adc5b131ca41d6f71
parent1f355178d6b05c9ba14c010b4304835801345f6a (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.h22
-rw-r--r--src/soc/intel/common/block/smbus/Kconfig4
-rw-r--r--src/soc/intel/common/block/smbus/Makefile.inc9
-rw-r--r--src/soc/intel/common/block/smbus/smbus.c101
-rw-r--r--src/soc/intel/common/block/smbus/smbus_early.c52
-rw-r--r--src/soc/intel/common/block/smbus/smbuslib.c137
-rw-r--r--src/soc/intel/common/block/smbus/smbuslib.h38
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 */