[coreboot-gerrit] Patch set updated for coreboot: drivers/intel/txe: Add HECI driver

Andrey Petrov (andrey.petrov@intel.com) gerrit at coreboot.org
Thu Mar 2 23:32:34 CET 2017


Andrey Petrov (andrey.petrov at intel.com) just uploaded a new patch set to gerrit, which you can find at https://review.coreboot.org/18547

-gerrit

commit 5aad623189ee886f07853ebcd25156399169bc18
Author: Andrey Petrov <andrey.petrov at intel.com>
Date:   Wed Mar 1 15:51:57 2017 -0800

    drivers/intel/txe: Add HECI driver
    
    Add common driver that can send/receive HECI messages. This driver is
    inspired by Linux kernel mei driver and somewhat based on Skylake's.
    Currently it has been only tested on Apollolake.
    
    BUG=b:35586975
    BRANCH=reef
    TEST=tested on Apollolake to send single messages and receive both
    fragmented and non-fragmented versions.
    
    Change-Id: Ie3772700270f4f333292b80d59f79555851780f7
    Signed-off-by: Andrey Petrov <andrey.petrov at intel.com>
---
 src/drivers/intel/txe/Kconfig      |   5 +
 src/drivers/intel/txe/Makefile.inc |   2 +
 src/drivers/intel/txe/txe.c        | 396 +++++++++++++++++++++++++++++++++++++
 src/drivers/intel/txe/txe.h        |  58 ++++++
 4 files changed, 461 insertions(+)

diff --git a/src/drivers/intel/txe/Kconfig b/src/drivers/intel/txe/Kconfig
new file mode 100644
index 0000000..be5ceb1
--- /dev/null
+++ b/src/drivers/intel/txe/Kconfig
@@ -0,0 +1,5 @@
+config TXE_COMMON
+	bool
+	default n
+	help
+	  Unified TXE driver
diff --git a/src/drivers/intel/txe/Makefile.inc b/src/drivers/intel/txe/Makefile.inc
new file mode 100644
index 0000000..4d7bf43
--- /dev/null
+++ b/src/drivers/intel/txe/Makefile.inc
@@ -0,0 +1,2 @@
+romstage-$(CONFIG_TXE_COMMON) += txe.c
+ramstage-$(CONFIG_TXE_COMMON) += txe.c
diff --git a/src/drivers/intel/txe/txe.c b/src/drivers/intel/txe/txe.c
new file mode 100644
index 0000000..5d20646
--- /dev/null
+++ b/src/drivers/intel/txe/txe.c
@@ -0,0 +1,396 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright 2017 Intel Inc.
+ *
+ * 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/early_variables.h>
+#include <commonlib/helpers.h>
+#include <console/console.h>
+#include <delay.h>
+#include <device/pci.h>
+#include <device/pci_ids.h>
+#include <device/pci_ops.h>
+#include <lib.h>
+#include <soc/pci_devs.h>
+#include <soc/pci_ids.h>
+#include <string.h>
+#include <timer.h>
+#include "txe.h"
+
+/* default for early boot */
+#define HECI1_BASE_ADDRESS				0xfed1a000
+
+#define HECI_DELAY_READY				(15 * 1000)
+#define HECI_DELAY					100
+#define HECI_SEND_TIMEOUT				(5 * 1000)
+#define HECI_READ_TIMEOUT				(5 * 1000)
+
+#define SLOT_SIZE					4
+
+struct txe_device {
+    uintptr_t sec_bar;
+} g_txe CAR_GLOBAL;
+
+void heci_init(uintptr_t bar) {
+
+	struct txe_device *txe = car_get_var_ptr(&g_txe);
+	device_t dev = HECI1_DEV;
+	u8 pcireg;
+
+	if (!bar)
+		bar = HECI1_BASE_ADDRESS;
+
+	/* assume initialized, nothing to do */
+	if (txe->sec_bar)
+		return;
+
+	/* Assign Resources to HECI1 */
+	/* Clear BIT 1-2 of Command Register */
+	pcireg = pci_read_config8(dev, PCI_COMMAND);
+	pcireg &= ~(PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY);
+	pci_write_config8(dev, PCI_COMMAND, pcireg);
+
+	/* Program Temporary BAR for HECI1 */
+	pci_write_config32(dev, PCI_BASE_ADDRESS_0, bar);
+	pci_write_config32(dev, PCI_BASE_ADDRESS_1, 0x0);
+
+	/* Enable Bus Master and MMIO Space */
+	pcireg = pci_read_config8(dev, PCI_COMMAND);
+	pcireg |= PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY;
+	pci_write_config8(dev, PCI_COMMAND, pcireg);
+
+	txe->sec_bar = bar;
+}
+
+static uint32_t read_bar(uint32_t offset)
+{
+	struct txe_device *txe = car_get_var_ptr(&g_txe);
+	return read32((void *)(txe->sec_bar + offset));
+}
+
+static void write_bar(uint32_t offset, uint32_t val)
+{
+	struct txe_device *txe = car_get_var_ptr(&g_txe);
+	return write32((void *)(txe->sec_bar + offset), val);
+}
+
+static uint32_t read_me_csr(void)
+{
+	return read_bar(MMIO_ME_CSR);
+}
+
+static uint32_t read_host_csr(void)
+{
+	return read_bar(MMIO_HOST_CSR);
+}
+
+static void write_host_csr(uint32_t data)
+{
+	write_bar(MMIO_HOST_CSR, data);
+}
+
+static uint8_t get_cb_msg_count(uint32_t data)
+{
+	int8_t read_offset = (data >> 8);
+	int8_t write_offset = (data >> 16);
+	return write_offset - read_offset;
+}
+
+static uint32_t me_filled_slots(void)
+{
+	return get_cb_msg_count(read_me_csr());
+}
+
+static uint8_t host_filled_slots(void)
+{
+	union me_csr csr;
+	csr.data = read_me_csr();
+
+	return csr.fields.me_cir_buff - get_cb_msg_count(csr.data);
+}
+
+static void clear_int(void)
+{
+	union me_csr csr;
+	csr.data = read_host_csr();
+	csr.fields.int_sts = 1;
+	write_host_csr(csr.data);
+}
+
+static uint32_t read_slot(void)
+{
+	return read_bar(MMIO_ME_CB_RW);
+}
+
+static void write_slot(uint32_t val)
+{
+	write_bar(MMIO_ME_CB_WW, val);
+}
+
+static int wait_write_slots(uint32_t cnt)
+{
+	struct stopwatch sw;
+
+	stopwatch_init_msecs_expire(&sw, HECI_READ_TIMEOUT);
+	while (host_filled_slots() < cnt) {
+		udelay(HECI_DELAY);
+		if (stopwatch_expired(&sw)) {
+			printk(BIOS_ERR, "HECI: timeout, buffer not drained\n");
+			return 0;
+		}
+	}
+	return 1;
+}
+
+static int wait_read_slots(uint32_t cnt)
+{
+	struct stopwatch sw;
+
+	stopwatch_init_msecs_expire(&sw, HECI_READ_TIMEOUT);
+	while (me_filled_slots() < cnt) {
+		udelay(HECI_DELAY);
+		if (stopwatch_expired(&sw)) {
+			printk(BIOS_ERR, "HECI: timed out reading answer!\n");
+			return 0;
+		}
+	}
+	return 1;
+}
+
+/* get number of full 4-byte slots */
+static uint32_t bytes_to_slots(uint32_t bytes)
+{
+	return ALIGN_UP(bytes, SLOT_SIZE) / SLOT_SIZE;
+}
+
+static int me_ready(void)
+{
+	union me_csr csr;
+	csr.data = read_me_csr();
+	return !!csr.fields.host_ready;
+}
+
+static int wait_heci_ready(void)
+{
+	struct stopwatch sw;
+
+	stopwatch_init_msecs_expire(&sw, HECI_DELAY_READY);
+	while (!me_ready()) {
+		udelay(HECI_DELAY);
+		if (stopwatch_expired(&sw))
+			return 0;
+	}
+
+	return 1;
+}
+
+static void host_gen_interrupt(void)
+{
+	union me_csr csr;
+	csr.data = read_host_csr();
+	csr.fields.int_gen = 1;
+	write_host_csr(csr.data);
+}
+
+static int
+send_one_message(union mei_header *hdr, void *buff)
+{
+	uint32_t pend_len, pend_slots, i, remainder, tmp;
+	uint32_t *p = (uint32_t*)buff;
+
+	/* get space for the header */
+	wait_write_slots(1);
+
+	/* first, write header */
+	write_slot(hdr->data);
+
+	pend_len = hdr->fields.length;
+	pend_slots = bytes_to_slots(pend_len);
+
+	wait_write_slots(pend_slots);
+
+	/* write the body in whole slots */
+	while (i < ALIGN_DOWN(pend_len, SLOT_SIZE)) {
+		write_slot(*p++);
+		i += SLOT_SIZE;
+	}
+
+	remainder = pend_len % SLOT_SIZE;
+	/* pad to 4 bytes not touching caller's buffer */
+	if (remainder) {
+		memcpy(&tmp, p, remainder);
+		write_slot(tmp);
+	}
+
+	host_gen_interrupt();
+
+	if (!me_ready())
+		return 0;
+
+	return hdr->fields.length;
+}
+
+int heci_send(void *msg, uint32_t len, uint8_t host, uint8_t client)
+{
+	union me_csr csr;
+	union mei_header hdr;
+	uint32_t sent = 0, remaining, slots, cb_size;
+	uint8_t *p = (uint8_t*) msg;
+
+	if (!msg || !len)
+		return 0;
+
+	clear_int();
+
+	if (!wait_heci_ready()) {
+		printk(BIOS_ERR, "HECI: not ready\n");
+		return 0;
+	}
+
+	csr.data = read_me_csr();
+	/* reserve one slot for header */
+	cb_size = (csr.fields.me_cir_buff - 1) * SLOT_SIZE - 1;
+
+	remaining = len;
+	slots = len / SLOT_SIZE;
+
+	/*
+	 * Fragment the message into smaller messages not exceeding useful
+	 * circullar buffer length. Mark last message complete.
+	 */
+	do {
+		hdr.fields.length = MIN(cb_size, remaining);
+		hdr.fields.client_address = client;
+		hdr.fields.host_address = host;
+		hdr.fields.is_complete = (remaining <= cb_size);
+		sent = send_one_message(&hdr, p);
+		p += sent;
+		remaining -= sent;
+	} while (remaining > 0 && sent != 0);
+
+	return remaining==0;
+}
+
+static uint32_t
+recv_one_message(union mei_header *hdr, void *buff, uint32_t maxlen)
+{
+	uint32_t *p = (uint32_t *)buff;
+	uint32_t recv_slots, recv_len, reminder, reg, i;
+
+	/* first get the header */
+	if (!wait_read_slots(1))
+		return 0;
+
+	hdr->data = read_slot();
+	recv_len = hdr->fields.length;
+
+	if (!recv_len)
+		printk(BIOS_WARNING, "HECI: message is zero-sized\n");
+
+	recv_slots = bytes_to_slots(recv_len);
+
+	if (recv_len > maxlen) {
+		printk(BIOS_ERR, "HECI: response is too big\n");
+		return 0;
+	}
+
+	/* wait for the rest of messages to arrive */
+	wait_read_slots(recv_slots);
+
+	/* fetch whole slots first */
+	while (i < ALIGN_DOWN(recv_len, 4)) {
+		*p++ = read_slot();
+		i += SLOT_SIZE;
+	}
+
+	reminder = recv_len % SLOT_SIZE;
+
+	if (reminder) {
+		reg = read_slot();
+		memcpy(p, &reg, reminder);
+	}
+
+	return recv_len;
+}
+
+int heci_receive(void *buff, uint32_t *maxlen)
+{
+	union mei_header hdr;
+	uint32_t left, received;
+	uint8_t *p = (uint8_t *)buff;
+
+	if (!buff || !maxlen || !*maxlen)
+		return 0;
+
+	left = *maxlen;
+
+	clear_int();
+
+	if (!wait_heci_ready()) {
+		printk(BIOS_ERR, "HECI: not ready\n");
+		return 0;
+	}
+
+	/*
+	 * Receive multiple packets until we meet one marked complete or we run
+	 * or we run out of space.
+	 */
+	do {
+		received = recv_one_message(&hdr, p, left);
+		left -= received;
+		p += received;
+		/* if read out all, ping to send more */
+		if (!hdr.fields.is_complete && !me_filled_slots())
+			host_gen_interrupt();
+	} while (received && !hdr.fields.is_complete && left > 0);
+
+	*maxlen = p - (uint8_t*) buff;
+
+	/* if ME is not ready, something went wrong and we received junk */
+	if (!me_ready())
+		return 0;
+
+	return hdr.fields.is_complete;
+}
+
+#if ENV_RAMSTAGE
+
+static void update_sec_bar(struct device *dev)
+{
+	g_txe.sec_bar = find_resource(dev, PCI_BASE_ADDRESS_0)->base;
+}
+
+static void txe_set_resources(struct device *dev)
+{
+	if (dev->path.pci.devfn == HECI1_DEVFN)
+		update_sec_bar(dev);
+
+	pci_dev_set_resources(dev);
+}
+
+static struct device_operations txe_ops = {
+	.set_resources		= txe_set_resources,
+	.read_resources		= pci_dev_read_resources,
+	.enable_resources	= pci_dev_enable_resources,
+	.init			= pci_dev_init,
+	.enable_resources	= pci_dev_enable_resources
+};
+
+static const struct pci_driver txe_driver __pci_driver = {
+	.ops			= &txe_ops,
+	.vendor			= PCI_VENDOR_ID_INTEL,
+	/* expectation is that soc/chipset code provides this */
+	.device			= PCI_DEVICE_ID_HECI1
+};
+
+#endif
diff --git a/src/drivers/intel/txe/txe.h b/src/drivers/intel/txe/txe.h
new file mode 100644
index 0000000..b837dbf
--- /dev/null
+++ b/src/drivers/intel/txe/txe.h
@@ -0,0 +1,58 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2017 Intel Corp.
+ *
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 _DRIVERS_INTEL_TXE_H_
+#define _DRIVERS_INTEL_TXE_H_
+
+#define MMIO_ME_CB_RW						0x08
+#define MMIO_ME_CSR						0x0c
+
+#define MMIO_ME_CB_WW						0x00
+#define MMIO_HOST_CSR						0x04
+
+union mei_header {
+	u32 data;
+	struct {
+	u32 client_address: 8;
+	u32 host_address: 8;
+	u32 length: 9;
+	u32 reserved: 6;
+	u32 is_complete: 1;
+	} __attribute__ ((packed)) fields;
+};
+
+union me_csr {
+	u32 data;
+	struct {
+	u32 int_en: 1;
+	u32 int_sts: 1;
+	u32 int_gen: 1;
+	u32 host_ready: 1;
+	u32 host_reset: 1;
+	u32 rsv: 3;
+	u32 me_read_offset: 8;
+	u32 me_write_offset: 8;
+	u32 me_cir_buff: 8;
+	} __attribute__ ((packed)) fields;
+};
+
+/* set up device for use in early boot enviroument with temp bar */
+void heci_init(uintptr_t bar);
+/* receive not less than maxlen into buff, returns 0 on failure */
+int heci_receive(void *buff, uint32_t *maxlen);
+/* send message of size len, returns 0 on failure */
+int heci_send(void *msg, uint32_t len, uint8_t host, uint8_t client);
+#endif // _DRIVERS_INTEL_TXE_H_



More information about the coreboot-gerrit mailing list