Matt DeVillier (matt.devillier(a)gmail.com) just uploaded a new patch set to gerrit, which you can find at https://review.coreboot.org/17718
-gerrit
commit cecd3b221067fb9a1e82a40ff2b53a871bab2f26
Author: Matt DeVillier <matt.devillier(a)gmail.com>
Date: Wed Nov 16 23:37:43 2016 -0600
soc/broadwell: set EM4/EM5 registers based on cdclk
The EM4/EM5 registers in the mini-HD audio device must be set based
on the GPU cdclk value in order for HDMI audio to function properly.
Add variables to save the correct values when initializing the GPU,
and accessor functions to retrieve them in order to set the registers
when initializing the mini-HD device.
Also fix a GPU-type check which meant to cap ULX GPUs cdclk value but
incorrectly checked for ULT instead.
Change-Id: Icce7d5981f0b2ccb09d3861b28b843a260c8aeba
Signed-off-by: Matt DeVillier <matt.devillier(a)gmail.com>
---
src/soc/intel/broadwell/igd.c | 23 +++++++++++++++++++----
src/soc/intel/broadwell/include/soc/igd.h | 22 ++++++++++++++++++++++
src/soc/intel/broadwell/minihd.c | 5 +++++
3 files changed, 46 insertions(+), 4 deletions(-)
diff --git a/src/soc/intel/broadwell/igd.c b/src/soc/intel/broadwell/igd.c
index ccb1e93..5c5e8bf 100644
--- a/src/soc/intel/broadwell/igd.c
+++ b/src/soc/intel/broadwell/igd.c
@@ -31,6 +31,7 @@
#include <soc/systemagent.h>
#include <soc/intel/broadwell/chip.h>
#include <vboot/vbnv.h>
+#include <soc/igd.h>
#define GT_RETRY 1000
#define GT_CDCLK_337 0
@@ -38,6 +39,12 @@
#define GT_CDCLK_540 2
#define GT_CDCLK_675 3
+static u32 reg_em4;
+static u32 reg_em5;
+
+u32 igd_get_reg_em4(void) { return reg_em4; }
+u32 igd_get_reg_em5(void) { return reg_em5; }
+
struct reg_script haswell_early_init_script[] = {
/* Enable Force Wake */
REG_RES_WRITE32(PCI_BASE_ADDRESS_0, 0xa180, 0x00000020),
@@ -398,6 +405,7 @@ static void igd_cdclk_init_haswell(struct device *dev)
static void igd_cdclk_init_broadwell(struct device *dev)
{
config_t *conf = dev->chip_info;
+ int devid = pci_read_config16(dev, PCI_DEVICE_ID);
int cdclk = conf->cdclk;
u32 dpdiv, lpcll, pwctl, cdset;
@@ -407,8 +415,7 @@ static void igd_cdclk_init_broadwell(struct device *dev)
gtt_write(0x138124, 0x80000018);
/* Poll GT driver mailbox for run/busy clear */
- if (!gtt_poll(0x138124, (1 << 31), (0 << 31)))
- cdclk = GT_CDCLK_450;
+ gtt_poll(0x138124, (1 << 31), (0 << 31));
if (gtt_read(0x42014) & 0x1000000) {
/* If CD clock is fixed then set to 450MHz */
@@ -421,8 +428,8 @@ static void igd_cdclk_init_broadwell(struct device *dev)
cdclk = GT_CDCLK_675;
}
- /* CD clock frequency 675MHz not supported on ULT */
- if (cpu_is_ult() && cdclk == GT_CDCLK_675)
+ /* CD clock frequency 675MHz not supported on ULX */
+ if (devid == IGD_BROADWELL_Y_GT2 && cdclk == GT_CDCLK_675)
cdclk = GT_CDCLK_540;
/* Set variables based on CD Clock setting */
@@ -432,24 +439,32 @@ static void igd_cdclk_init_broadwell(struct device *dev)
lpcll = (1 << 27);
pwctl = 2;
dpdiv = 169;
+ reg_em4 = 16;
+ reg_em5 = 225;
break;
case GT_CDCLK_450:
cdset = 449;
lpcll = 0;
pwctl = 0;
dpdiv = 225;
+ reg_em4 = 4;
+ reg_em5 = 75;
break;
case GT_CDCLK_540:
cdset = 539;
lpcll = (1 << 26);
pwctl = 1;
dpdiv = 270;
+ reg_em4 = 4;
+ reg_em5 = 90;
break;
case GT_CDCLK_675:
cdset = 674;
lpcll = (1 << 26) | (1 << 27);
pwctl = 3;
dpdiv = 338;
+ reg_em4 = 8;
+ reg_em5 = 225;
default:
return;
}
diff --git a/src/soc/intel/broadwell/include/soc/igd.h b/src/soc/intel/broadwell/include/soc/igd.h
new file mode 100644
index 0000000..a0db5ef
--- /dev/null
+++ b/src/soc/intel/broadwell/include/soc/igd.h
@@ -0,0 +1,22 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2012 Chromium OS Authors
+ *
+ * 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_BROADWELL_GMA_H
+#define SOC_INTEL_BROADWELL_GMA_H
+
+u32 igd_get_reg_em4(void);
+u32 igd_get_reg_em5(void);
+
+#endif /* SOC_INTEL_BROADWELL_GMA_H */
\ No newline at end of file
diff --git a/src/soc/intel/broadwell/minihd.c b/src/soc/intel/broadwell/minihd.c
index 5014b08..a9fc3d6 100644
--- a/src/soc/intel/broadwell/minihd.c
+++ b/src/soc/intel/broadwell/minihd.c
@@ -25,6 +25,7 @@
#include <stdlib.h>
#include <soc/intel/common/hda_verb.h>
#include <soc/ramstage.h>
+#include <soc/igd.h>
static const u32 minihd_verb_table[] = {
/* coreboot specific header */
@@ -101,6 +102,10 @@ static void minihd_init(struct device *dev)
minihd_verb_table);
}
}
+
+ /* Set EM4/EM5 registers */
+ write32(base + 0x0100c, igd_get_reg_em4());
+ write32(base + 0x01010, igd_get_reg_em5());
}
static struct device_operations minihd_ops = {
Matt DeVillier (matt.devillier(a)gmail.com) just uploaded a new patch set to gerrit, which you can find at https://review.coreboot.org/17719
-gerrit
commit cca16ab430cbe671ffe9f6e3e5027305ea4c6cc5
Author: Matt DeVillier <matt.devillier(a)gmail.com>
Date: Fri Dec 2 23:27:32 2016 -0600
google/beltino: fix LED for Tricky variant
Polarity value for Tricky's power LED was incorrect for the 'on' state;
fix using correct value from Chromium source
TEST: power on Tricky, observe LED lit / solid
Change-Id: I8bc7c4ae3f83d3f37b76fd5c90a4faed7057ebee
Signed-off-by: Matt DeVillier <matt.devillier(a)gmail.com>
---
src/mainboard/google/beltino/variants/tricky/led.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/mainboard/google/beltino/variants/tricky/led.c b/src/mainboard/google/beltino/variants/tricky/led.c
index 72498a3..9bf561d 100644
--- a/src/mainboard/google/beltino/variants/tricky/led.c
+++ b/src/mainboard/google/beltino/variants/tricky/led.c
@@ -25,7 +25,7 @@ void set_power_led(int state)
it8772f_gpio_led(IT8772F_GPIO_DEV,
2 /* set */,
0xF7 /* select */,
- state /* polarity: state dependent */,
+ 0x04 * state /* polarity: state dependent */,
0x00 /* 0=pulldown */,
0x04 /* output */,
0x04 /* 1=Simple IO function */,
Matt DeVillier (matt.devillier(a)gmail.com) just uploaded a new patch set to gerrit, which you can find at https://review.coreboot.org/17718
-gerrit
commit 867cacf4b88696a212d0c03240cbb2c8ff4d3c83
Author: Matt DeVillier <matt.devillier(a)gmail.com>
Date: Wed Nov 16 23:37:43 2016 -0600
soc/broadwell: set EM4/EM5 registers based on cdclk
The EM4/EM5 registers in the mini-HD audio device must be set based
on the GPU cdclk value in order for HDMI audio to function properly.
Add variables to save the correct values when initializing the GPU,
and accessor functions to retrieve them in order to set the registers
when initializing the mini-HD device.
Also fix a GPU-type check which meant to cap ULX GPUs cdclk value but
incorrectly checked for ULT instead.
Change-Id: Icce7d5981f0b2ccb09d3861b28b843a260c8aeba
Signed-off-by: Matt DeVillier <matt.devillier(a)gmail.com>
---
src/soc/intel/broadwell/igd.c | 22 ++++++++++++++++++----
src/soc/intel/broadwell/include/soc/igd.h | 22 ++++++++++++++++++++++
src/soc/intel/broadwell/minihd.c | 5 +++++
3 files changed, 45 insertions(+), 4 deletions(-)
diff --git a/src/soc/intel/broadwell/igd.c b/src/soc/intel/broadwell/igd.c
index ccb1e93..9c05876 100644
--- a/src/soc/intel/broadwell/igd.c
+++ b/src/soc/intel/broadwell/igd.c
@@ -38,6 +38,12 @@
#define GT_CDCLK_540 2
#define GT_CDCLK_675 3
+static u32 reg_em4;
+static u32 reg_em5;
+
+u32 igd_get_reg_em4(void) { return reg_em4; }
+u32 igd_get_reg_em5(void) { return reg_em5; }
+
struct reg_script haswell_early_init_script[] = {
/* Enable Force Wake */
REG_RES_WRITE32(PCI_BASE_ADDRESS_0, 0xa180, 0x00000020),
@@ -398,6 +404,7 @@ static void igd_cdclk_init_haswell(struct device *dev)
static void igd_cdclk_init_broadwell(struct device *dev)
{
config_t *conf = dev->chip_info;
+ int devid = pci_read_config16(dev, PCI_DEVICE_ID);
int cdclk = conf->cdclk;
u32 dpdiv, lpcll, pwctl, cdset;
@@ -407,8 +414,7 @@ static void igd_cdclk_init_broadwell(struct device *dev)
gtt_write(0x138124, 0x80000018);
/* Poll GT driver mailbox for run/busy clear */
- if (!gtt_poll(0x138124, (1 << 31), (0 << 31)))
- cdclk = GT_CDCLK_450;
+ gtt_poll(0x138124, (1 << 31), (0 << 31));
if (gtt_read(0x42014) & 0x1000000) {
/* If CD clock is fixed then set to 450MHz */
@@ -421,8 +427,8 @@ static void igd_cdclk_init_broadwell(struct device *dev)
cdclk = GT_CDCLK_675;
}
- /* CD clock frequency 675MHz not supported on ULT */
- if (cpu_is_ult() && cdclk == GT_CDCLK_675)
+ /* CD clock frequency 675MHz not supported on ULX */
+ if (devid == IGD_BROADWELL_Y_GT2 && cdclk == GT_CDCLK_675)
cdclk = GT_CDCLK_540;
/* Set variables based on CD Clock setting */
@@ -432,24 +438,32 @@ static void igd_cdclk_init_broadwell(struct device *dev)
lpcll = (1 << 27);
pwctl = 2;
dpdiv = 169;
+ reg_em4 = 16;
+ reg_em5 = 225;
break;
case GT_CDCLK_450:
cdset = 449;
lpcll = 0;
pwctl = 0;
dpdiv = 225;
+ reg_em4 = 4;
+ reg_em5 = 75;
break;
case GT_CDCLK_540:
cdset = 539;
lpcll = (1 << 26);
pwctl = 1;
dpdiv = 270;
+ reg_em4 = 4;
+ reg_em5 = 90;
break;
case GT_CDCLK_675:
cdset = 674;
lpcll = (1 << 26) | (1 << 27);
pwctl = 3;
dpdiv = 338;
+ reg_em4 = 8;
+ reg_em5 = 225;
default:
return;
}
diff --git a/src/soc/intel/broadwell/include/soc/igd.h b/src/soc/intel/broadwell/include/soc/igd.h
new file mode 100644
index 0000000..a0db5ef
--- /dev/null
+++ b/src/soc/intel/broadwell/include/soc/igd.h
@@ -0,0 +1,22 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2012 Chromium OS Authors
+ *
+ * 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_BROADWELL_GMA_H
+#define SOC_INTEL_BROADWELL_GMA_H
+
+u32 igd_get_reg_em4(void);
+u32 igd_get_reg_em5(void);
+
+#endif /* SOC_INTEL_BROADWELL_GMA_H */
\ No newline at end of file
diff --git a/src/soc/intel/broadwell/minihd.c b/src/soc/intel/broadwell/minihd.c
index 5014b08..a9fc3d6 100644
--- a/src/soc/intel/broadwell/minihd.c
+++ b/src/soc/intel/broadwell/minihd.c
@@ -25,6 +25,7 @@
#include <stdlib.h>
#include <soc/intel/common/hda_verb.h>
#include <soc/ramstage.h>
+#include <soc/igd.h>
static const u32 minihd_verb_table[] = {
/* coreboot specific header */
@@ -101,6 +102,10 @@ static void minihd_init(struct device *dev)
minihd_verb_table);
}
}
+
+ /* Set EM4/EM5 registers */
+ write32(base + 0x0100c, igd_get_reg_em4());
+ write32(base + 0x01010, igd_get_reg_em5());
}
static struct device_operations minihd_ops = {
Aaron Durbin (adurbin(a)chromium.org) just uploaded a new patch set to gerrit, which you can find at https://review.coreboot.org/17716
-gerrit
commit ee80c786ec2f45285266c3bf96c82cf5764a5d9f
Author: Aaron Durbin <adurbin(a)chromium.org>
Date: Sat Dec 3 17:08:08 2016 -0600
commonlib: provide incoherent region device
The MRC cache uses an incoherent mechanism for updating the
cache contents in that it assumes memory mapped boot device
access for checking against latest data for update. However,
it uses another driver for updating the underlying storage
area.
In order to aid in moving the MRC cache over to using
region_devices for updates provide an implementation of
a region_device which performs reads and writes to different
region_devices so that different drivers can be used
transparently.
BUG=chrome-os-partner:56151
Change-Id: I30e858245c30cbccd6313aff5ebecd3fd98d7302
Signed-off-by: Aaron Durbin <adurbin(a)chromium.org>
---
src/commonlib/include/commonlib/region.h | 23 ++++++++++
src/commonlib/region.c | 79 ++++++++++++++++++++++++++++++++
2 files changed, 102 insertions(+)
diff --git a/src/commonlib/include/commonlib/region.h b/src/commonlib/include/commonlib/region.h
index bc3ded1..3f7d3bd 100644
--- a/src/commonlib/include/commonlib/region.h
+++ b/src/commonlib/include/commonlib/region.h
@@ -245,4 +245,27 @@ void xlate_region_device_rw_init(struct xlate_region_device *xdev,
size_t sub_offset, size_t sub_size,
size_t parent_size);
+/* This type can be used for incoherent access where the read and write
+ * operations are backed by separate drivers. An example is x86 systems
+ * with memory mapped media for reading but use a spi flash driver for
+ * writing. One needs to ensure using this object is appropriate in context. */
+struct incoherent_rdev {
+ struct region_device rdev;
+ const struct region_device *read;
+ const struct region_device *write;
+};
+
+/* Initialize an incoherent_rdev based on the region as well as the read and
+ * write rdevs. The read and write rdevs should match in size to the passed
+ * in region. If not the initialization will fail returning NULL. Otherwise
+ * the function will return a pointer to the containing region_device to
+ * be used for region operations. Therefore, the lifetime of the returned
+ * pointer matches the lifetime of the incoherent_rdev object. Likewise,
+ * the lifetime of the read and write rdev need to match the lifetime of
+ * the incoherent_rdev object. */
+const struct region_device *incoherent_rdev_init(struct incoherent_rdev *irdev,
+ const struct region *r,
+ const struct region_device *read,
+ const struct region_device *write);
+
#endif /* _REGION_H_ */
diff --git a/src/commonlib/region.c b/src/commonlib/region.c
index ac0faf1..bf53b9d 100644
--- a/src/commonlib/region.c
+++ b/src/commonlib/region.c
@@ -437,3 +437,82 @@ const struct region_device_ops xlate_rdev_rw_ops = {
.writeat = xlate_writeat,
.eraseat = xlate_eraseat,
};
+
+
+static void *incoherent_mmap(const struct region_device *rd, size_t offset,
+ size_t size)
+{
+ const struct incoherent_rdev *irdev;
+
+ irdev = container_of(rd, const struct incoherent_rdev, rdev);
+
+ return rdev_mmap(irdev->read, offset, size);
+}
+
+static int incoherent_munmap(const struct region_device *rd, void *mapping)
+{
+ const struct incoherent_rdev *irdev;
+
+ irdev = container_of(rd, const struct incoherent_rdev, rdev);
+
+ return rdev_munmap(irdev->read, mapping);
+}
+
+static ssize_t incoherent_readat(const struct region_device *rd, void *b,
+ size_t offset, size_t size)
+{
+ const struct incoherent_rdev *irdev;
+
+ irdev = container_of(rd, const struct incoherent_rdev, rdev);
+
+ return rdev_readat(irdev->read, b, offset, size);
+}
+
+static ssize_t incoherent_writeat(const struct region_device *rd, const void *b,
+ size_t offset, size_t size)
+{
+ const struct incoherent_rdev *irdev;
+
+ irdev = container_of(rd, const struct incoherent_rdev, rdev);
+
+ return rdev_writeat(irdev->write, b, offset, size);
+}
+
+static ssize_t incoherent_eraseat(const struct region_device *rd, size_t offset,
+ size_t size)
+{
+ const struct incoherent_rdev *irdev;
+
+ irdev = container_of(rd, const struct incoherent_rdev, rdev);
+
+ return rdev_eraseat(irdev->write, offset, size);
+}
+
+static const struct region_device_ops incoherent_rdev_ops = {
+ .mmap = incoherent_mmap,
+ .munmap = incoherent_munmap,
+ .readat = incoherent_readat,
+ .writeat = incoherent_writeat,
+ .eraseat = incoherent_eraseat,
+};
+
+const struct region_device *incoherent_rdev_init(struct incoherent_rdev *irdev,
+ const struct region *r,
+ const struct region_device *read,
+ const struct region_device *write)
+{
+ const size_t size = region_sz(r);
+
+ if (size != region_device_sz(read) || size != region_device_sz(write))
+ return NULL;
+
+ /* The region is represented as offset 0 to size. That way, the generic
+ * rdev operations can be called on the read or write implementation
+ * without any unnecessary translation because the offsets all start
+ * at 0. */
+ region_device_init(&irdev->rdev, &incoherent_rdev_ops, 0, size);
+ irdev->read = read;
+ irdev->write = write;
+
+ return &irdev->rdev;
+}
Aaron Durbin (adurbin(a)chromium.org) just uploaded a new patch set to gerrit, which you can find at https://review.coreboot.org/17716
-gerrit
commit d5260b5a8d463983472da9de6355cbada80a49bd
Author: Aaron Durbin <adurbin(a)chromium.org>
Date: Sat Dec 3 17:08:08 2016 -0600
commonlib: provide incoherent region device
The MRC cache uses an incoherent mechanism for updating the
cache contents in that it assumes memory mapped boot device
access for checking against latest data for update. However,
it uses another driver for updating the underlying storage
area.
In order to aid in moving the MRC cache over to using
region_devices for updates provide an implementation of
a region_device which performs reads and writes to different
region_devices so that different drivers can be used
transparently.
BUG=chrome-os-partner:56151
Change-Id: I30e858245c30cbccd6313aff5ebecd3fd98d7302
Signed-off-by: Aaron Durbin <adurbin(a)chromium.org>
---
src/commonlib/include/commonlib/region.h | 23 ++++++++++
src/commonlib/region.c | 79 ++++++++++++++++++++++++++++++++
2 files changed, 102 insertions(+)
diff --git a/src/commonlib/include/commonlib/region.h b/src/commonlib/include/commonlib/region.h
index bc3ded1..6b6116f 100644
--- a/src/commonlib/include/commonlib/region.h
+++ b/src/commonlib/include/commonlib/region.h
@@ -245,4 +245,27 @@ void xlate_region_device_rw_init(struct xlate_region_device *xdev,
size_t sub_offset, size_t sub_size,
size_t parent_size);
+/* This type can be used for incoherent access where the read and write
+ * operations are backed by separate drivers. An example is x86 systems
+ * with memory mapped media for reading but use a spi flash driver for
+ * writing. One needs to ensure using this object is appropriate in context. */
+struct incoherent_rdev {
+ struct region_device rdev;
+ const struct region_device *read;
+ const struct region_device *write;
+};
+
+/* Initialize an incoherent_rdev based on the region as well as the read and
+ * write rdevs. The read and write rdevs should match in size to the passed
+ * in region. If not the initialization will fail returning NULL. Otherwise
+ * the function will return a pointer to the containing region_device to
+ * be used for region operations. Therefore, the lifetime of the returned
+ * pointer matches the lifetime of the incoherent_rdev object. Likewise,
+ * the lifetime of the read and write rdev need to match the lifetime of
+ * the incoherent_rdev object. */
+const struct region_device *incoherent_rdev_init(struct incoherent_rdev *irdev,
+ const struct region *r,
+ const struct region_device *read,
+ const struct region_device *write);
+
#endif /* _REGION_H_ */
diff --git a/src/commonlib/region.c b/src/commonlib/region.c
index ac0faf1..ae03f77 100644
--- a/src/commonlib/region.c
+++ b/src/commonlib/region.c
@@ -437,3 +437,82 @@ const struct region_device_ops xlate_rdev_rw_ops = {
.writeat = xlate_writeat,
.eraseat = xlate_eraseat,
};
+
+
+static void *incoherent_mmap(const struct region_device *rd, size_t offset,
+ size_t size)
+{
+ const struct incoherent_rdev *irdev;
+
+ irdev = container_of(rd, const struct incoherent_rdev, rdev);
+
+ return rdev_mmap(irdev->read, offset, size);
+}
+
+static int incoherent_munmap(const struct region_device *rd, void *mapping)
+{
+ const struct incoherent_rdev *irdev;
+
+ irdev = container_of(rd, const struct incoherent_rdev, rdev);
+
+ return rdev_munmap(irdev->read, mapping);
+}
+
+static ssize_t incoherent_readat(const struct region_device *rd, void *b,
+ size_t offset, size_t size)
+{
+ const struct incoherent_rdev *irdev;
+
+ irdev = container_of(rd, const struct incoherent_rdev, rdev);
+
+ return rdev_readat(irdev->read, b, offset, size);
+}
+
+static ssize_t incoherent_writeat(const struct region_device *rd, const void *b,
+ size_t offset, size_t size)
+{
+ const struct incoherent_rdev *irdev;
+
+ irdev = container_of(rd, const struct incoherent_rdev, rdev);
+
+ return rdev_writeat(irdev->write, b, offset, size);
+}
+
+static ssize_t incoherent_eraseat(const struct region_device *rd, size_t offset,
+ size_t size)
+{
+ const struct incoherent_rdev *irdev;
+
+ irdev = container_of(rd, const struct incoherent_rdev, rdev);
+
+ return rdev_eraseat(irdev->write, offset, size);
+}
+
+static const struct region_device_ops incoherent_rdev_ops = {
+ .mmap = incoherent_mmap,
+ .munmap = incoherent_munmap,
+ .readat = incoherent_readat,
+ .writeat = incoherent_writeat,
+ .eraseat = incoherent_eraseat,
+};
+
+const struct region_device *incoherent_rdev_init(struct incoherent_rdev *irdev,
+ const struct region *r,
+ const struct region_device *read,
+ const struct region_device *write)
+{
+ const size_t size = region_sz(r);
+
+ if (size != region_device_sz(read) || size != region_device_sz(write))
+ return NULL;
+
+ /* The region is represented as offset 0 to size. That way, the generic
+ * rdev operations can be called on the read or write implementation
+ * without any unnecessary translation because the offsets all start
+ * at 0. */
+ region_device_init(&irdev->rdev, &incoherent_rdev_ops, 0, size);
+ irdev->read = read;
+ irdev->write = write;
+
+ return &irdev->rdev;
+}
Aaron Durbin (adurbin(a)chromium.org) just uploaded a new patch set to gerrit, which you can find at https://review.coreboot.org/17715
-gerrit
commit adc6af11a45601c10210a4956c37936080a2372a
Author: Aaron Durbin <adurbin(a)chromium.org>
Date: Sat Dec 3 17:04:06 2016 -0600
drivers/spi: provide a mechanism to obtain the SPI flash boot device
The MRC cache wants to be able to access the SPI flash boot device.
Allow an easy way to provide that so that there isn't duplicate
spi_flash objects representing the same device.
BUG=chrome-os-partner:56151
Change-Id: Iba92e8bb8a6060cdd327b10f5f8ec23ac61101e7
Signed-off-by: Aaron Durbin <adurbin(a)chromium.org>
---
src/drivers/spi/boot_device_rw_nommap.c | 6 ++++++
src/include/spi_flash.h | 4 ++++
2 files changed, 10 insertions(+)
diff --git a/src/drivers/spi/boot_device_rw_nommap.c b/src/drivers/spi/boot_device_rw_nommap.c
index a52a7be..e7d9b9a 100644
--- a/src/drivers/spi/boot_device_rw_nommap.c
+++ b/src/drivers/spi/boot_device_rw_nommap.c
@@ -95,3 +95,9 @@ const struct region_device *boot_device_rw(void)
return &spi_rw;
}
+
+const struct spi_flash *boot_device_spi_flash(void)
+{
+ boot_device_rw_init();
+ return car_get_var(sfg);
+}
diff --git a/src/include/spi_flash.h b/src/include/spi_flash.h
index 8fec484..95b8251 100644
--- a/src/include/spi_flash.h
+++ b/src/include/spi_flash.h
@@ -87,4 +87,8 @@ int spi_flash_volatile_group_end(const struct spi_flash *flash);
int chipset_volatile_group_begin(const struct spi_flash *flash);
int chipset_volatile_group_end(const struct spi_flash *flash);
+/* Return spi_flash object reference for the boot device. This is only valid
+ * if CONFIG_BOOT_DEVICE_SPI_FLASH is enabled. */
+const struct spi_flash *boot_device_spi_flash(void);
+
#endif /* _SPI_FLASH_H_ */
Aaron Durbin (adurbin(a)chromium.org) just uploaded a new patch set to gerrit, which you can find at https://review.coreboot.org/17714
-gerrit
commit f8efb13093bd4ab2cd2aebf39c8394647317b59b
Author: Aaron Durbin <adurbin(a)chromium.org>
Date: Sat Dec 3 21:05:53 2016 -0600
lib/compute_ip_checksum: mark data buffer as const
compute_ip_checksum() doesn't manipulate the data it is passed.
Therefore, mark it as const.
BUG=chrome-os-partner:56151
Change-Id: I54cff9695a886bacd6314aa441d96aaa7a991101
Signed-off-by: Aaron Durbin <adurbin(a)chromium.org>
---
src/include/ip_checksum.h | 2 +-
src/lib/compute_ip_checksum.c | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/include/ip_checksum.h b/src/include/ip_checksum.h
index a1e0ffa..f6ef560 100644
--- a/src/include/ip_checksum.h
+++ b/src/include/ip_checksum.h
@@ -1,5 +1,5 @@
#ifndef IP_CHECKSUM_H
#define IP_CHECKSUM_H
-unsigned long compute_ip_checksum(void *addr, unsigned long length);
+unsigned long compute_ip_checksum(const void *addr, unsigned long length);
unsigned long add_ip_checksums(unsigned long offset, unsigned long sum, unsigned long new);
#endif /* IP_CHECKSUM_H */
diff --git a/src/lib/compute_ip_checksum.c b/src/lib/compute_ip_checksum.c
index 58a6bf1..347b83f 100644
--- a/src/lib/compute_ip_checksum.c
+++ b/src/lib/compute_ip_checksum.c
@@ -1,9 +1,9 @@
#include <stdint.h>
#include <ip_checksum.h>
-unsigned long compute_ip_checksum(void *addr, unsigned long length)
+unsigned long compute_ip_checksum(const void *addr, unsigned long length)
{
- uint8_t *ptr;
+ const uint8_t *ptr;
volatile union {
uint8_t byte[2];
uint16_t word;
Aaron Durbin (adurbin(a)chromium.org) just uploaded a new patch set to gerrit, which you can find at https://review.coreboot.org/17713
-gerrit
commit 39fe9e5c19fe0f542a65c6301a534f306c8a4f5e
Author: Aaron Durbin <adurbin(a)chromium.org>
Date: Sat Nov 19 12:36:09 2016 -0600
lib: add region file support
The region file library is added to provide the underpinnings for
other libraries that support appending updates when the data changes.
The most recent written data is deemed the latest data associated
with that "file". A good example is the MRC cache which in a follow up
patch utilizes this library.
BUG=chrome-os-partner:56151
Change-Id: Ic3caf1edbb6f11dbbe27181a87b7b19d1224fffa
Signed-off-by: Aaron Durbin <adurbin(a)chromium.org>
---
src/include/region_file.h | 56 ++++++
src/lib/Makefile.inc | 2 +
src/lib/region_file.c | 470 ++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 528 insertions(+)
diff --git a/src/include/region_file.h b/src/include/region_file.h
new file mode 100644
index 0000000..6341b00
--- /dev/null
+++ b/src/include/region_file.h
@@ -0,0 +1,56 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright 2016 Google 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.
+ */
+
+#ifndef REGION_FILE_H
+#define REGION_FILE_H
+
+#include <commonlib/region.h>
+#include <stdint.h>
+
+/* A region file is an abstraction to allow appending updates in a
+ * region_device where the data returned is the most recently data
+ * written. It is block based so if you write 2 bytes the data
+ * region_device could be 16 bytes. */
+
+struct regf;
+
+/*
+ * Initialize a region file associated with a provided region device.
+ * Returns < 0 on error, 0 on success.
+ */
+int regf_init(const struct region_device *p, struct regf *f);
+
+/*
+ * Fill in region device associated with latest update of file data.
+ * Returns < 0 on error, 0 on success.
+ */
+int regf_data(const struct regf *f, struct region_device *rdev);
+
+/* Update region file with latest data. Returns < 0 on error, 0 on success. */
+int regf_update_data(struct regf *f, const void *buf, size_t size);
+
+/* Declared here for easy object allocation. */
+struct regf {
+ /* Region device covering file */
+ struct region_device rdev;
+ /* Metadata containg blocks of the data stream. */
+ struct region_device metadata;
+ /* Blocks forming data. */
+ uint16_t data_blocks[2];
+ /* Current slot in metadata marking end of data. */
+ int slot;
+};
+
+#endif /* REGION_FILE_H */
diff --git a/src/lib/Makefile.inc b/src/lib/Makefile.inc
index 55d19d0..2d5aa57 100644
--- a/src/lib/Makefile.inc
+++ b/src/lib/Makefile.inc
@@ -87,6 +87,8 @@ romstage-$(CONFIG_PRIMITIVE_MEMTEST) += primitive_memtest.c
ramstage-$(CONFIG_PRIMITIVE_MEMTEST) += primitive_memtest.c
romstage-$(CONFIG_CACHE_AS_RAM) += ramtest.c
romstage-$(CONFIG_GENERIC_GPIO_LIB) += gpio.c
+ramstage-y += region_file.c
+romstage-y += region_file.c
ramstage-y += romstage_handoff.c
romstage-y += romstage_handoff.c
romstage-y += romstage_stack.c
diff --git a/src/lib/region_file.c b/src/lib/region_file.c
new file mode 100644
index 0000000..50f4412
--- /dev/null
+++ b/src/lib/region_file.c
@@ -0,0 +1,470 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright 2016 Google 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 <commonlib/helpers.h>
+#include <console/console.h>
+#include <region_file.h>
+#include <string.h>
+
+/*
+ * A region file provides generic support for appending new data
+ * within a storage region. The book keeping is tracked in metadata
+ * blocks where an offset pointer points to the last byte of a newly
+ * allocated byte sequence. Thus, by taking 2 block offets one can
+ * determine start and size of the latest update. The data does not
+ * have to be the same consistent size, but the data size has be small
+ * enough to fit a metadata block and one data write within the region.
+ *
+ * The granularity of the block offsets are 16 bytes. By using 16-bit
+ * block offsets a region's total size can be no larger than 1MiB.
+ * However, the last 32 bytes cannot be used in the 1MiB maximum region
+ * because one needs to put a block offset indicating last byte written.
+ * An unused block offset is the value 0xffff or 0xffff0 bytes. The last
+ * block offset that can be written is 0xfffe or 0xfffe0 byte offset.
+ *
+ * The goal of this library is to provide a simple mechanism for
+ * allocating blocks of data for updates. The metadata is written first
+ * followed by the data. That means a power event between the block offset
+ * write and the data write results in blocks being allocated but not
+ * entirely written. It's up to the user of the library to sanity check
+ * data stored.
+ */
+
+#define REGF_BLOCK_SHIFT 4
+#define REGF_BLOCK_GRANULARITY (1 << REGF_BLOCK_SHIFT)
+#define REGF_METADATA_BLOCK_SIZE REGF_BLOCK_GRANULARITY
+#define REGF_UNALLOCATED_BLOCK 0xffff
+#define REGF_UPDATES_PER_METADATA_BLOCK \
+ (REGF_METADATA_BLOCK_SIZE / sizeof(uint16_t))
+
+enum {
+ RF_ONLY_METADATA = 0,
+ RF_EMPTY = -1,
+ RF_NEED_TO_EMPTY = -2,
+ RF_FATAL = -3,
+};
+
+struct metadata_block {
+ uint16_t blocks[REGF_UPDATES_PER_METADATA_BLOCK];
+};
+
+static size_t block_to_bytes(uint16_t offset)
+{
+ return (size_t)offset << REGF_BLOCK_SHIFT;
+}
+
+static size_t bytes_to_block(size_t bytes)
+{
+ return bytes >> REGF_BLOCK_SHIFT;
+}
+
+static inline int block_offset_unallocated(uint16_t offset)
+{
+ return offset == REGF_UNALLOCATED_BLOCK;
+}
+
+static inline size_t regf_data_begin(const struct regf *f)
+{
+ return f->data_blocks[0];
+}
+
+static inline size_t regf_data_end(const struct regf *f)
+{
+ return f->data_blocks[1];
+}
+
+static int all_block_offsets_unallocated(const struct metadata_block *mb)
+{
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(mb->blocks); i++)
+ if (!block_offset_unallocated(mb->blocks[i]))
+ return 0;
+
+ return 1;
+}
+
+/* Read metadata block at block i. */
+static int read_mb(size_t i, struct metadata_block *mb, const struct regf *f)
+{
+ size_t offset = block_to_bytes(i);
+
+ if (rdev_readat(&f->metadata, mb, offset, sizeof(*mb)) < 0)
+ return -1;
+
+ return 0;
+}
+
+/* Locate metadata block with the latest update */
+static int find_latest_mb(struct metadata_block *mb, size_t num_mb_blocks,
+ struct regf *f)
+{
+ size_t l = 0;
+ size_t r = num_mb_blocks;
+
+ while (l + 1 < r) {
+ size_t mid = (l + r) / 2;
+
+ if (read_mb(mid, mb, f) < 0)
+ return -1;
+ if (all_block_offsets_unallocated(mb))
+ r = mid;
+ else
+ l = mid;
+ }
+
+ /* Set the base block slot. */
+ f->slot = l * REGF_UPDATES_PER_METADATA_BLOCK;
+
+ /* Re-read metadata block with the latest update. */
+ if (read_mb(l, mb, f) < 0)
+ return -1;
+
+ return 0;
+}
+
+static void find_latest_slot(struct metadata_block *mb, struct regf *f)
+{
+ size_t i;
+
+ for (i = REGF_UPDATES_PER_METADATA_BLOCK - 1; i > 0; i--) {
+ if (!block_offset_unallocated(mb->blocks[i]))
+ break;
+ }
+
+ f->slot += i;
+}
+
+static int fill_data_boundaries(struct regf *f)
+{
+ struct region_device slots;
+ size_t offset;
+ size_t size = sizeof(f->data_blocks);
+
+ if (f->slot == RF_ONLY_METADATA) {
+ size_t start = bytes_to_block(region_device_sz(&f->metadata));
+ f->data_blocks[0] = start;
+ f->data_blocks[1] = start;
+ return 0;
+ }
+
+ /* Sanity check the 2 slot sequence to read. If it's out of the
+ * metadata blocks' bounds then one needs to empty. This is done
+ * to uniquely identify I/O vs data errors in the readat() below. */
+ offset = (f->slot - 1) * sizeof(f->data_blocks[0]);
+ if (rdev_chain(&slots, &f->metadata, offset, size)) {
+ f->slot = RF_NEED_TO_EMPTY;
+ return 0;
+ }
+
+ if (rdev_readat(&slots, &f->data_blocks, 0, size) < 0) {
+ printk(BIOS_ERR, "REGF failed to read data boundaries.\n");
+ return -1;
+ }
+
+ /* All used blocks should be incrementing from previous write. */
+ if (regf_data_begin(f) >= regf_data_end(f)) {
+ printk(BIOS_ERR, "REGF data boundaries wrong. [%zd,%zd) Need to empty.\n",
+ regf_data_begin(f), regf_data_end(f));
+ f->slot = RF_NEED_TO_EMPTY;
+ return 0;
+ }
+
+ /* Ensure data doesn't exceed the region. */
+ if (regf_data_end(f) > bytes_to_block(region_device_sz(&f->rdev))) {
+ printk(BIOS_ERR, "REGF data exceeds region %zd > %zd\n",
+ regf_data_end(f),
+ bytes_to_block(region_device_sz(&f->rdev)));
+ f->slot = RF_NEED_TO_EMPTY;
+ }
+
+ return 0;
+}
+
+int regf_init(const struct region_device *p, struct regf *f)
+{
+ struct metadata_block mb;
+
+ /* Total number of metadata blocks is found by reading the first
+ * block offset as the metadata is allocated first. At least one
+ * metadata block is available. */
+
+ memset(f, 0, sizeof(*f));
+ f->slot = RF_FATAL;
+
+ /* Keep parent around for accessing data later. */
+ if (rdev_chain(&f->rdev, p, 0, region_device_sz(p)))
+ return -1;
+
+ if (rdev_readat(p, &mb, 0, sizeof(mb)) < 0) {
+ printk(BIOS_ERR, "REGF fail reading first metadata block.\n");
+ return -1;
+ }
+
+ /* No metadata has been allocated. Assume region is empty. */
+ if (block_offset_unallocated(mb.blocks[0])) {
+ f->slot = RF_EMPTY;
+ return 0;
+ }
+
+ /* If metadata block is 0 in size then need to empty. */
+ if (mb.blocks[0] == 0) {
+ f->slot = RF_NEED_TO_EMPTY;
+ return 0;
+ }
+
+ /* The region needs to be emptied as the metadata is broken. */
+ if (rdev_chain(&f->metadata, p, 0, block_to_bytes(mb.blocks[0]))) {
+ f->slot = RF_NEED_TO_EMPTY;
+ return 0;
+ }
+
+ /* Locate latest metadata block with latest update. */
+ if (find_latest_mb(&mb, mb.blocks[0], f)) {
+ printk(BIOS_ERR, "REGF fail locating latest metadata block.\n");
+ f->slot = RF_FATAL;
+ return -1;
+ }
+
+ find_latest_slot(&mb, f);
+
+ /* Fill in the data blocks marking the latest upate. */
+ if (fill_data_boundaries(f)) {
+ printk(BIOS_ERR, "REGF fail locating data boundaries.\n");
+ f->slot = RF_FATAL;
+ return -1;
+ }
+
+ return 0;
+}
+
+int regf_data(const struct regf *f, struct region_device *rdev)
+{
+
+ size_t offset;
+ size_t size;
+
+ /* Slot indicates if any data is available. */
+ if (f->slot <= RF_ONLY_METADATA)
+ return -1;
+
+ offset = block_to_bytes(regf_data_begin(f));
+ size = block_to_bytes(regf_data_end(f)) - offset;
+
+ return rdev_chain(rdev, &f->rdev, offset, size);
+}
+
+/*
+ * Allocate enough metadata blocks to maximize data updates. Do this in
+ * terms of blocks. To solve the balance of metadata vs data, 2 linear
+ * equations are solved in terms of blocks where 'x' is number of
+ * data updates and 'y' is number of metadata blocks:
+ *
+ * x = number of data updates
+ * y = number of metadata blocks
+ * T = total blocks in region
+ * D = data size in blocks
+ * M = metadata size in blocks
+ * A = updates accounted for in each metadata block
+ *
+ * T = D * x + M * y
+ * y = x / A
+ * -----------------
+ * T = D * x + M * x / A = x * (D + M / A)
+ * T * A = x * (D * A + M)
+ * x = T * A / (D * A + M)
+ */
+static int allocate_metadata(struct regf *f, size_t data_blks)
+{
+ size_t t, m;
+ size_t x, y;
+ uint16_t tot_metadata;
+ const size_t a = REGF_UPDATES_PER_METADATA_BLOCK;
+ const size_t d = data_blks;
+
+ t = bytes_to_block(ALIGN_DOWN(region_device_sz(&f->rdev),
+ REGF_BLOCK_GRANULARITY));
+ m = bytes_to_block(ALIGN_UP(REGF_METADATA_BLOCK_SIZE,
+ REGF_BLOCK_GRANULARITY));
+
+ /* Ensure at least one data update can fit with 1 metadata block
+ * within the region. */
+ if (d > t - m)
+ return -1;
+
+ /* Maximize number of updates by aligning up to the number updates in
+ * a metadata block. May not really be able to achieve the number of
+ * updates in practice, but it ensures enough metata blocks are
+ * allocated. */
+ x = ALIGN_UP(t * a / (d * a + m), a);
+
+ /* One data block has to fit. */
+ if (x == 0)
+ x = 1;
+
+ /* Now calculate how many metadata blocks are needed. */
+ y = ALIGN_UP(x, a) / a;
+
+ /* Need to commit the metadata allocation. */
+ tot_metadata = m * y;
+ if (rdev_writeat(&f->rdev, &tot_metadata, 0, sizeof(tot_metadata)) < 0)
+ return -1;
+
+ if (rdev_chain(&f->metadata, &f->rdev, 0,
+ block_to_bytes(tot_metadata)))
+ return -1;
+
+ /* Initialize a 0 data block to start appending from. */
+ f->data_blocks[0] = tot_metadata;
+ f->data_blocks[1] = tot_metadata;
+
+ return 0;
+}
+
+static int update_can_fit(const struct regf *f, size_t data_blks)
+{
+ size_t metadata_slots;
+ size_t end_blk;
+
+ metadata_slots = region_device_sz(&f->metadata) / sizeof(uint16_t);
+
+ /* No more slots. */
+ if ((size_t)f->slot + 1 >= metadata_slots)
+ return 0;
+
+ /* See where the last block lies from the curent one. */
+ end_blk = data_blks + regf_data_end(f);
+
+ /* Update would have exceded block addressing. */
+ if (end_blk >= REGF_UNALLOCATED_BLOCK)
+ return 0;
+
+ /* End block exceeds size of region. */
+ if (end_blk > bytes_to_block(region_device_sz(&f->rdev)))
+ return 0;
+
+ return 1;
+}
+
+static int commit_data_allocation(struct regf *f, size_t data_blks)
+{
+ size_t offset;
+
+ f->slot++;
+
+ offset = f->slot * sizeof(uint16_t);
+ f->data_blocks[0] = regf_data_end(f);
+ f->data_blocks[1] = regf_data_begin(f) + data_blks;
+
+ if (rdev_writeat(&f->metadata, &f->data_blocks[1], offset,
+ sizeof(f->data_blocks[1])) < 0)
+ return -1;
+
+ return 0;
+}
+
+static int commit_data(const struct regf *f, const void *buf, size_t size)
+{
+ size_t offset = block_to_bytes(regf_data_begin(f));
+ if (rdev_writeat(&f->rdev, buf, offset, size) < 0)
+ return -1;
+ return 0;
+}
+
+static int handle_empty(struct regf *f, size_t data_blks)
+{
+ if (allocate_metadata(f, data_blks)) {
+ printk(BIOS_ERR, "REGF metadata allocation failed: %zd data blocks %zd total blocks\n",
+ data_blks, bytes_to_block(region_device_sz(&f->rdev)));
+ return -1;
+ }
+
+ f->slot = RF_ONLY_METADATA;
+
+ return 0;
+}
+
+static int handle_need_to_empty(struct regf *f)
+{
+ if (rdev_eraseat(&f->rdev, 0, region_device_sz(&f->rdev)) < 0) {
+ printk(BIOS_ERR, "REGF empty failed.\n");
+ return -1;
+ }
+
+ f->slot = RF_EMPTY;
+
+ return 0;
+}
+
+static int handle_update(struct regf *f, size_t blocks, const void *buf,
+ size_t size)
+{
+ if (!update_can_fit(f, blocks)) {
+ printk(BIOS_INFO, "REGF update can't fit. Will empty.\n");
+ f->slot = RF_NEED_TO_EMPTY;
+ return 0;
+ }
+
+ if (commit_data_allocation(f, blocks)) {
+ printk(BIOS_ERR, "REGF failed to commit data allocation.\n");
+ return -1;
+ }
+
+ if (commit_data(f, buf, size)) {
+ printk(BIOS_ERR, "REGF failed to commit data.\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+int regf_update_data(struct regf *f, const void *buf, size_t size)
+{
+ int ret;
+ size_t blocks;
+
+ blocks = bytes_to_block(ALIGN_UP(size, REGF_BLOCK_GRANULARITY));
+
+ while (1) {
+ int prev_slot = f->slot;
+
+ switch (f->slot) {
+ case RF_EMPTY:
+ ret = handle_empty(f, blocks);
+ break;
+ case RF_NEED_TO_EMPTY:
+ ret = handle_need_to_empty(f);
+ break;
+ case RF_FATAL:
+ ret = -1;
+ break;
+ default:
+ ret = handle_update(f, blocks, buf, size);
+ break;
+ }
+
+ /* Failing case. No more updates allowed to be attempted. */
+ if (ret) {
+ f->slot = RF_FATAL;
+ break;
+ }
+
+ /* No more state changes and data commited. */
+ if (f->slot > RF_ONLY_METADATA && prev_slot != f->slot)
+ break;
+ }
+
+ return ret;
+}