aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/commonlib/include/commonlib/region.h23
-rw-r--r--src/commonlib/region.c79
2 files changed, 102 insertions, 0 deletions
diff --git a/src/commonlib/include/commonlib/region.h b/src/commonlib/include/commonlib/region.h
index bc3ded1d0a..3f7d3bd52f 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 ac0faf111f..bf53b9dda8 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;
+}